Apache Airflow timetable driven by a cron expression on the Jalali (Persian / Solar Hijri) calendar.
Project description
airflow-jalali-cron
A custom Apache Airflow timetable that schedules DAGs
with an ordinary cron expression whose day-of-month and month fields are read on the
Jalali (Persian / Solar Hijri) calendar, in the Asia/Tehran timezone. The default
(JalaliCron()) runs at the start of every Jalali month.
Distributing the timetable as an installable package is the approach recommended by Airflow: it gives the timetable class one stable import path that is available to every Airflow component (scheduler, webserver/API server, workers, triggerer, DAG processor), and that same path is what gets serialized into the DAG — which avoids the "timetable not registered / cannot import" errors you hit when loading it straight from the plugins folder.
Installation
Install it into the same Python environment as Airflow (every node that parses or runs DAGs):
pip install airflow-jalali-cron
That single install does two things:
- Makes
JalaliCronimportable in your DAG files. - Auto-registers the plugin with Airflow via the
airflow.pluginsentry point — so you do not need to copy anything into$AIRFLOW_HOME/plugins.
Usage
from datetime import datetime
from airflow import DAG
from airflow.operators.empty import EmptyOperator
from airflow_jalali_cron import JalaliCron
with DAG(
dag_id="jalali_monthly_report",
start_date=datetime(2024, 3, 20), # ~ start of a Jalali year
timetable=JalaliCron("0 9 1 * *"), # 09:00 on the 1st of each Jalali month
catchup=False,
tags=["jalali"],
):
EmptyOperator(task_id="run_monthly_job")
After installing, confirm Airflow sees the plugin:
airflow plugins
You should see jalali_cron_plugin listed with JalaliCron under its
timetables.
Schedule syntax
JalaliCron(cron, *, timezone="Asia/Tehran", clamp_overflow_days=True)
The cron is a standard 5-field expression — minute hour day-of-month month day-of-week — supporting *, lists (,), ranges (-) and steps (*/n, a-b/n).
What's special:
- day-of-month and month are interpreted on the Jalali calendar (month 1 = Farvardin … 12 = Esfand).
- minute / hour are plain
Asia/Tehranwall-clock time. - day-of-week uses the Iranian week (same numbering as
jdatetime):0=Saturday (shanbe),1=Sunday,2=Monday,3=Tuesday,4=Wednesday,5=Thursday,6=Friday (jome).
| Goal | cron |
|---|---|
| Start of every Jalali month (default) | 0 0 1 * * |
| 09:30 on the 5th of each month | 30 9 5 * * |
| Every other month (Farvardin, Khordad, …) | 0 0 1 */2 * |
| Every third month (Farvardin, Tir, Mehr, Dey) | 0 0 1 */3 * |
| Noon every Friday | 0 12 * * 6 |
| Last day of every month | 0 0 31 * * |
clamp_overflow_days (default True): a day-of-month larger than a month's length
is clamped to the last day — the effective day is min(day, month_length). So
0 0 31 * * fires on the 31st in months 1–6, the 30th in months 7–11, and the 29th/30th
in Esfand. Set it to False for standard cron behavior, where an out-of-range day simply
never fires in shorter months.
How it works
- Semantics are interval-based, matching Airflow's
CronDataIntervalTimetable: each run covers[this firing, next firing)and is triggered at the end of that interval. - All times are computed in
Asia/Tehran(configurable viatimezone). - With
catchup=False, the timetable fast-forwards to the current interval instead of backfilling every period sincestart_date. - When both day-of-month and day-of-week are restricted, a day matches if either does (the standard Vixie-cron rule).
Compatibility
- Airflow: 2.4+
- Python: 3.9–3.12 (matching Airflow 2.x support)
Development
python -m venv .venv && source .venv/bin/activate
pip install build twine
python -m build # creates dist/*.whl and dist/*.tar.gz
twine check dist/*
License
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file airflow_jalali_cron-0.1.0.tar.gz.
File metadata
- Download URL: airflow_jalali_cron-0.1.0.tar.gz
- Upload date:
- Size: 10.0 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
6b64b4caf1dac2da8940472bf61b9c0b469dace03843a2ded7d3f9c340400bb3
|
|
| MD5 |
32aa7aca06c87264e4c63d1e9600df7d
|
|
| BLAKE2b-256 |
aaa52f9215622d343d9a34a0f0359d515c4ee70a11f2097cd2a87ea5be6110b9
|
Provenance
The following attestation bundles were made for airflow_jalali_cron-0.1.0.tar.gz:
Publisher:
publish.yml on amirnaderi93/airflow-jalali-cron
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
airflow_jalali_cron-0.1.0.tar.gz -
Subject digest:
6b64b4caf1dac2da8940472bf61b9c0b469dace03843a2ded7d3f9c340400bb3 - Sigstore transparency entry: 1705789802
- Sigstore integration time:
-
Permalink:
amirnaderi93/airflow-jalali-cron@6f56553a07bce0c3966cc1241db9ec28699b4945 -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/amirnaderi93
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@6f56553a07bce0c3966cc1241db9ec28699b4945 -
Trigger Event:
release
-
Statement type:
File details
Details for the file airflow_jalali_cron-0.1.0-py3-none-any.whl.
File metadata
- Download URL: airflow_jalali_cron-0.1.0-py3-none-any.whl
- Upload date:
- Size: 9.6 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
8d755d49c2a7c2741f6a4b0a204430a0fe781b7e172ee02e3a92bbe0f6720c5a
|
|
| MD5 |
707e02a8bcb57b91679c1389148bf9d4
|
|
| BLAKE2b-256 |
5d9fe7ee2fd41abc8ede7d8540debcf9d27620d826daf57bdc1d97a09f922e6c
|
Provenance
The following attestation bundles were made for airflow_jalali_cron-0.1.0-py3-none-any.whl:
Publisher:
publish.yml on amirnaderi93/airflow-jalali-cron
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
airflow_jalali_cron-0.1.0-py3-none-any.whl -
Subject digest:
8d755d49c2a7c2741f6a4b0a204430a0fe781b7e172ee02e3a92bbe0f6720c5a - Sigstore transparency entry: 1705789825
- Sigstore integration time:
-
Permalink:
amirnaderi93/airflow-jalali-cron@6f56553a07bce0c3966cc1241db9ec28699b4945 -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/amirnaderi93
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@6f56553a07bce0c3966cc1241db9ec28699b4945 -
Trigger Event:
release
-
Statement type: