New to Kestra?
Use blueprints to kickstart your first workflows.
Schedule Kestra tasks to run only on business days using Python and country holiday calendars. An advanced scheduling pattern for enterprise and financial jobs.
Most schedulers fire on a fixed cron and have no idea whether the date is a weekend or a national holiday. This blueprint adds a business-day gate to your schedule: a Python script checks the run date against a country-specific holiday calendar (using the workalendar library) and stops the flow cleanly when the date is not a working day. It is a practical pattern for billing runs, financial processing, reporting jobs, and data pipelines that must follow local business calendars rather than a naive every-day trigger.
io.kestra.plugin.core.trigger.Schedule trigger fires on the cron 0 14 25 12 * and passes the execution date into the flow.check_if_business_date task (io.kestra.plugin.scripts.python.Commands) runs python schedule.py "{{trigger.date ?? inputs.date}}" {{inputs.country}} on the io.kestra.plugin.core.runner.Process runner with namespaceFiles enabled, installing the workalendar dependency at runtime.{{ trigger.date ?? inputs.date }} expression uses the scheduled date in production and falls back to the date input for manual testing.log task (io.kestra.plugin.core.log.Log) confirms the flow continues. If it is a weekend or public holiday, the script exits with an error and downstream tasks never run.country input (US and FR provided, easily extended).date input without changing production behavior.A plain cron scheduler cannot answer "is today actually a working day in this country?" Kestra fills that gap by letting an event-driven Schedule trigger hand the date to a real Python check, then express the whole pipeline as declarative YAML. You also get automatic retries, full execution lineage and logs, and the ability to chain real downstream work after the gate, none of which a bare cron entry provides.
workalendar Python package (installed automatically via the task dependencies).schedule.py Namespace File (see Quick start).This blueprint uses no secrets. The flow runs entirely on the schedule date and the country input.
schedule.py as a Namespace File:import sys
from datetime import datetime
from workalendar.europe import France
from workalendar.usa import UnitedStates
def is_business_day(date_str, country_calendar):
date = datetime.fromisoformat(date_str.replace("Z", ""))
return country_calendar.is_working_day(date)
def main():
if len(sys.argv) != 3:
print("Usage: script.py <date> <country_code>")
sys.exit(1)
date_str, country_code = sys.argv[1], sys.argv[2]
calendars = {"FR": France(), "US": UnitedStates()}
country_calendar = calendars.get(country_code.upper())
if not country_calendar:
print(f"Calendar for '{country_code}' not supported or not found.")
sys.exit(1)
try:
if not is_business_day(date_str, country_calendar):
print(f"{date_str} is not a business day in {country_code}.")
sys.exit(1)
print(f"{date_str} is a business day in {country_code}.")
except Exception as e:
print(f"Error: {e}")
sys.exit(1)
if __name__ == "__main__":
main()
country input (defaults to US) and optionally a date for manual runs.calendars dictionary in schedule.py using other workalendar regions.log task with your real downstream work (a dbt run, a data load, a report).errors branch to alert when a run is skipped.