Skip to main content

Custom Timetables

This package provides a set of custom Airflow timetables for advanced scheduling scenarios. Below you'll find usage instructions and examples for each timetable class.

Originally written by Jorge Marquez (jmmarqu/airflow-custom-timetables). Maintained by Gary Dukes (pendulum .now fix, optional holiday calendars, packaging).


Install

pip install "airflow-custom-timetables[holidays]"

Or in requirements.txt:

airflow-custom-timetables[holidays]

From GitHub instead of PyPI:

airflow-custom-timetables[holidays] @ git+https://github.com/airflow-dev/airflow-custom-timetables.git

Then pip install -r requirements.txt (or however you build the Airflow image). Import path is unchanged:

from custom_timetables import BusinessDayOfMonth

Installing the package registers the Airflow plugin CustomIntervalTimetables via the airflow.plugins entry point, so you do not need to copy this file into $AIRFLOW_HOME/plugins. Drop it in plugins/ or pip-install it — not both.

apache-airflow is not a pip dependency of this package; install it into an Airflow environment that already has Airflow and pendulum. The [holidays] extra pulls in PyPI holidays for optional country calendars.


How to Use

  1. Import the desired timetable class in your DAG file:

    from custom_timetables import (
        MonthlyLastDay, MonthlyOnDay, MonthlyMultipleDays, QuarterlyFirstDay, QuarterlyLastDay,
        YearlyFirstDay, YearlyWeekdayOccurrence, WeeklyOnDay, BiweeklyOnDay, SemiMonthly,
        EveryNDays, BusinessDayOfMonth, MonthlyLastDayExceptWeekend, CronTimetable, EveryNInterval,
    )
    
  2. Set the timetable in your DAG:

    from airflow import DAG
    
    dag = DAG(
        "my_dag",
        timetable=SemiMonthly(hour=8, minute=0),
    )
    

Timetable Classes and Examples

Pattern Class Sample Usage Description
Last day of month MonthlyLastDay MonthlyLastDay(hour=18, minute=0) Last day of each month at 18:00
Specific day of month MonthlyOnDay MonthlyOnDay(day=10, hour=9, minute=0) 10th of each month at 09:00
Multiple days in month MonthlyMultipleDays MonthlyMultipleDays(days=[1, 15], hour=8, minute=0) 1st and 15th of each month at 08:00
First day of quarter QuarterlyFirstDay QuarterlyFirstDay(hour=7, minute=0) First day of each quarter at 07:00
Last day of quarter QuarterlyLastDay QuarterlyLastDay(hour=17, minute=30) Last day of each quarter at 17:30
First day of year YearlyFirstDay YearlyFirstDay(hour=0, minute=0) January 1st each year at midnight
Nth weekday in month MonthlyWeekdayOccurrence MonthlyWeekdayOccurrence(weekday=0, n=2, hour=9, minute=0) 2nd Monday of each month at 09:00
Nth weekday in year/month YearlyWeekdayOccurrence YearlyWeekdayOccurrence(month=11, weekday=0, n=1, hour=9, minute=0) 1st Monday of November at 09:00
Weekly on specific weekday WeeklyOnDay WeeklyOnDay(weekday=2, hour=10, minute=0) Every Wednesday at 10:00
Biweekly on specific weekday BiweeklyOnDay BiweeklyOnDay(weekday=4, hour=8, minute=0) Every other Friday at 08:00
Semi-monthly (15th and last day) SemiMonthly SemiMonthly(hour=8, minute=0) 15th and last day of each month at 08:00
Every N days EveryNDays EveryNDays(interval_days=10, hour=7, minute=0) Every 10 days at 07:00
Nth business day of month BusinessDayOfMonth BusinessDayOfMonth(n=1, hour=9, minute=0) 1st business day of each month at 09:00
Last business day of month BusinessDayOfMonth BusinessDayOfMonth(n=-1, hour=17, minute=0) Last business day of each month at 17:00
First working day (England bank holidays) BusinessDayOfMonth BusinessDayOfMonth(n=1, hour=9, tz="Europe/London", country="GB", subdiv="ENG") 1st working day, skipping weekends and England holidays
Last day except weekend (move to Friday) MonthlyLastDayExceptWeekend MonthlyLastDayExceptWeekend(hour=18, minute=0) Last day of month at 18:00, or previous Friday
Cron expression CronTimetable CronTimetable("0 9 15 * *", timezone="America/New_York") 15th of every month at 09:00 (or any cron pattern)
Every N hours/minutes EveryNInterval EveryNInterval(interval_hours=6) or EveryNInterval(interval_minutes=45) Every 6 hours or every 45 minutes

Notes

  • All timetables accept a tz or timezone parameter (default: "America/New_York").
  • For weekday parameters: Monday=0, ..., Sunday=6.
  • For MonthlyWeekdayOccurrence and YearlyWeekdayOccurrence, n=-1 means "last" occurrence.
  • For BusinessDayOfMonth, n=-1 means "last business day".
  • BusinessDayOfMonth and MonthlyLastDayExceptWeekend accept optional country / subdiv / observed (ISO 3166, via PyPI holidays). Default country=None is weekends only and does not import holidays. Example: country="GB", subdiv="ENG" for England bank holidays. US state: country="US", subdiv="NY". The calendar is not stored on the timetable — only those three kwargs are serialized.

Optional holiday calendars

Install with the extra (or pip install holidays next to the plugin):

pip install "airflow-custom-timetables[holidays]"
from custom_timetables import BusinessDayOfMonth, MonthlyLastDayExceptWeekend

# Weekends only — same as before. holidays is not imported.
BusinessDayOfMonth(n=1, hour=9, tz="Europe/London")

# Skip England bank holidays as well as Saturday/Sunday.
BusinessDayOfMonth(
    n=1, hour=9, tz="Europe/London",
    country="GB", subdiv="ENG",
)

# Last weekday of the month, walking back over US federal holidays too.
MonthlyLastDayExceptWeekend(hour=18, country="US")

observed=True (the library default) also skips substitute weekdays (e.g. Monday when Christmas is a Sunday). Pass observed=False to use the calendar date only.

When a calendar is loaded, that country's weekend set is used (holidays calendar.weekend) instead of hardcoded Monday–Friday. Countries with a Friday–Saturday weekend therefore skip those days.


CronTimetable Usage Examples

For more advanced patterns, combine these classes or use CronTimetable for full cron flexibility.

The CronTimetable class allows you to use any cron expression for scheduling. You can specify the cron string and an optional timezone.

from custom_timetables import CronTimetable

# 15th of every month at 09:00
timetable = CronTimetable("0 9 15 * *", timezone="America/New_York")

# Every Monday and Wednesday at 18:30
timetable = CronTimetable("30 18 * * 1,3", timezone="America/New_York")

# Every day at midnight UTC
timetable = CronTimetable("0 0 * * *", timezone="UTC")

# Every weekday (Mon-Fri) at 07:15
timetable = CronTimetable("15 7 * * 1-5", timezone="America/New_York")

# Every 10 minutes
timetable = CronTimetable("*/10 * * * *", timezone="America/New_York")

Tip: You can use any valid cron expression supported by Airflow. See crontab.guru for help building cron expressions.


Development

pip install -e ".[dev]"
pytest

Airflow is stubbed in tests, so you do not need an Airflow install to run them. pendulum and holidays come in with the dev extra.

The pip import path is still custom_timetables. The package lives under src/custom_timetables/ (_base.py for the shared clock/period machinery, _timetables.py for the concrete classes).


License

MIT. Original work by Jorge Marquez; subsequent packaging, holiday calendars, and refactor by Gary Dukes. Source: airflow-dev/airflow-custom-timetables. See LICENSE.

Release files for airflow-custom-timetables 0.2.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for airflow-custom-timetables 0.2.0
File Size Uploaded
airflow_custom_timetables-0.2.0.tar.gz 17.2 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for airflow-custom-timetables 0.2.0
File Interpreter ABI Platform
airflow_custom_timetables-0.2.0-py3-none-any.whl Python 3 none any Details

Total release size:32.8 kB

Release files / airflow_custom_timetables-0.2.0.tar.gz

Download URL airflow_custom_timetables-0.2.0.tar.gz
Size 17.2 kB
Tags Source
SHA-256 checksum
How to use checksums
dbb2e623c3620287d6cff3ab61c0526f985348b5073de7e2b93b8ea9eac53a43
BLAKE2b-256 checksum
How to use checksums
26e69b3e95e77dbf73f33e8ab6de834e217125ebbee24b9ceb9eef6c52e52491
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.11.32 {"installer":{"name":"uv","version":"0.11.32","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"CachyOS Linux","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

Release files / airflow_custom_timetables-0.2.0-py3-none-any.whl

Download URL airflow_custom_timetables-0.2.0-py3-none-any.whl
Size 15.6 kB
Tags Python 3
SHA-256 checksum
How to use checksums
746f7ed9ccd2778bdc9dcfa3c770f949aa8427ddb82930182ed2e199df84686a
BLAKE2b-256 checksum
How to use checksums
b07f008dbbd38de5457ea93d68fccd76d7ce4d8d1127ecd2b4d085e0d883c114
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.11.32 {"installer":{"name":"uv","version":"0.11.32","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"CachyOS Linux","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

Release history Release notifications | RSS feed

This release

0.2.0 This release

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page