CronDelta
Compare actual cron engines before changing the library behind your schedules.
Changing a cron library, its version, or its options can change when a schedule fires. CronDelta calls two installed Python engines and compares their UTC occurrences in a finite window. It reports the first proven difference, the versions and timezone data used, and whether the window was fully checked. It can also show which UTC occurrences change when only the timezone database changes.
Quick start
Use Python 3.11+ and uv. Install the released CLI from PyPI. If the release is not yet available there, use the source installation below.
uv tool install crondelta==0.2.0
crondelta compare \
--left-engine croniter --left-version 6.2.4 \
--right-engine apscheduler --right-version 3.11.3 \
--expression '0 9 1 * mon' --timezone UTC \
--start 2026-10-01T00:00:00Z --end 2026-11-01T00:00:00Z
This returns DIFFERENT, with exit code 1. Croniter's default
day-of-month/day-of-week OR rule yields October 1, 5, 12, 19, and 26 at 09:00 UTC.
APScheduler's AND rule yields no occurrences in that window. The first witness
is October 1 at 09:00 UTC versus a fully checked empty right-hand stream.
Changing the expression to 0 9 * * mon returns MATCH_WITHIN_WINDOW, with
exit code 0. This means the streams match throughout the checked
[start, end) window; it makes no claim about dates outside that window.
A difference can reflect an intentional library contract.
For JSON output, add --format json. For multiple schedules, download the
migration manifest
and save it as migration.json in your current directory, then run:
crondelta compare --manifest migration.json --format json
The installed CLI does not include example manifests. More example manifests are in the repository. To build and install from source instead:
git clone https://github.com/0then0/crondelta.git
cd crondelta
uv build
uv tool install ./dist/crondelta-0.2.0-py3-none-any.whl
crondelta --version
Installation prepares dependencies. Running compare uses already available
interpreters and libraries; it never installs packages, creates environments,
executes jobs, or connects to a database.
Supported comparisons
- Engines: croniter and APScheduler 3.x
CronTrigger, using their public APIs. The locked baseline is croniter 6.2.4, APScheduler 3.11.3, and tzdata 2026.2. Historical UTC controls cover croniter 6.0.0 and APScheduler 3.11.2. Other installed versions can be requested explicitly. - Profiles: independent expressions, supported options, exact version requirements, and Python interpreters. This also supports comparing two versions of the same engine. Each target interpreter needs its engine and tzdata installed; it does not need a separate CronDelta installation.
- Expressions: five fields: minute, hour, day-of-month, month, day-of-week. Six/seven fields, aliases, random/hash fields, and APScheduler 4.x are outside the supported scope. Each engine parses its own expression.
- Timezones: an explicit IANA key, with both engines forced to use the
tzdata wheel. Reports record the requested and resolved tzdata version and the
SHA-256 of the exact TZif file used. Sides with different timezone data are
UNRESOLVEDunless the comparison is explicitly allowed with--allow-different-tzdata; see the tzdata upgrade case. - Evidence: exact UTC datetimes, original local time, offset, fold, and UTC
round-trip. Deadlines and output/occurrence limits bound enumeration. An
incomplete calculation cannot produce
MATCH_WITHIN_WINDOW.
CronDelta checks trigger calculations. Scheduler execution policies such as misfires, coalescing, jitter, and catch-up are outside its scope. It does not convert expressions or repair migrations automatically.
Documentation
- CLI reference: profiles, manifests, limits, exit codes, and interpreting incomplete results.
- Report and adapter protocol: JSON fields, evidence, and the enumeration contract, including start boundaries and DST.
- query-exporter migration case study: pinned upstream sources, controlled differences, and reproduction commands.
- Croniter upgrade case: croniter 6.0.0 to 6.2.4 across Zurich's repeated DST hour, with a UTC control.
- Timezone data upgrade case: the same croniter 6.2.4 and expression under tzdata 2026.5 and 2026.6 in Havana, with Berlin and UTC controls and an independent TZif check.
- Testing and support: verification commands, CI matrix, and tested behavior.
- Example manifests and representative JSON reports.
Related tools
cron-utils provides Java parsing, validation, dialect mapping, and execution-time calculations. cronkit provides cron humanization, inventories, auditing, timelines, and crontab diffs. cron-comparison compares and benchmarks JavaScript cron implementations against fixtures. CronDelta focuses on two installed Python profiles and reproducible evidence for a specific time window.
Contributing
See CONTRIBUTING.md for setup, validation, and reproducible evidence requirements. Participation follows the Code of Conduct. Report vulnerabilities privately as described in SECURITY.md. See CHANGELOG.md for released and unreleased changes.
Development
From a checkout:
uv sync --locked
uv run crondelta --version
sh scripts/verify.sh
This runs linting, tests, wheel/sdist builds, installed-package checks, and reproduction controls. See Testing and support for details.
Licensed under Apache-2.0.
Metadata
Release files for crondelta 0.2.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| crondelta-0.2.0.tar.gz | 89.6 kB | With provenance | Details for crondelta-0.2.0.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| crondelta-0.2.0-py3-none-any.whl | Python 3 | none | any | Details for crondelta-0.2.0-py3-none-any.whl |
Total release size: 114.6 kB
Release files / crondelta-0.2.0.tar.gz
| Download URL | crondelta-0.2.0.tar.gz |
|---|---|
| Size | 89.6 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
5031bdfca4837c4271993a7e60d29109613905d9bcd380097abc82fd5b234491
|
|
BLAKE2b-256 checksum How to use checksums |
7c8ff7b324821554f738d66b24120da22a0f589a42574bed1046d1e464cee24a
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.16
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Nov 1, 2026.
Transparency logRelease files / crondelta-0.2.0-py3-none-any.whl
| Download URL | crondelta-0.2.0-py3-none-any.whl |
|---|---|
| Size | 25.0 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
a470512183cb6c972b6c8b2d29729f1d845b9b8d74150ed0fd909b423242a748
|
|
BLAKE2b-256 checksum How to use checksums |
4867300fb134ea3ccd7e92e87baa0cbca6ba6fd3c83b2a43448f88bb3c40f6bb
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.16
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Nov 1, 2026.
Transparency logSecurity for crondelta 0.2.0
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
Full provenance
All 2 files in this release have been uploaded with provenance metadata.
Attestation for crondelta-0.2.0.tar.gz
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Nov 1, 2026.
Transparency logA note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.