A high-performance Rust implementation of croniter, providing up to 50x faster cron expression parsing and datetime iteration. Drop-in compatible with the original Python API.
Validated against 2.3 million differential fuzz tests with zero correctness bugs.
- Up to 50x faster — Rust-powered cron iteration, exposed to Python via PyO3
- Drop-in compatible — same API as croniter, swap one import line
- Rigorously tested — 2.3M differential fuzz tests, 5,200 real-world GitHub Actions expressions, 301 Rust unit tests, 95%+ code coverage
- Independently maintained — not a fork; a clean-room Rust reimplementation with its own test suite
pip install croniter-rs# Before
from croniter import croniter
# After — no other code changes needed
from croniter_rs import croniter# Graceful fallback (recommended)
try:
from croniter_rs import croniter # Fast Rust version
except ImportError:
from croniter import croniter # Fallback to original| Operation | croniter (Python) | croniter-rs (Rust) | Speedup |
|---|---|---|---|
| Constructor | 83.79 μs | 6.09 μs | 13.8x |
| get_next() | 24.62 μs | 0.52 μs | 47.0x |
| get_prev() | 24.37 μs | 0.49 μs | 49.9x |
| is_valid() | 79.77 μs | 5.27 μs | 15.1x |
| match() | 114.97 μs | 5.85 μs | 19.7x |
| 1000 iterations | 24.93 ms | 0.45 ms | 55.3x |
Benchmarks: Linux 6.8.0, Python 3.10, 10,000 iterations per operation.
from croniter_rs import croniter
from datetime import datetime
base = datetime(2024, 1, 1, 0, 0)
cron = croniter('*/5 * * * *', base)
# Get next/previous occurrence
print(cron.get_next(datetime)) # 2024-01-01 00:05:00
print(cron.get_prev(datetime)) # 2024-01-01 00:00:00
# Iterate
for _ in range(5):
print(cron.get_next(datetime))
# Validate expression
print(croniter.is_valid('0 0 * * *')) # True
# Check if datetime matches pattern
print(croniter.match('0 12 * * *', datetime(2024, 1, 1, 12, 0))) # Truecroniter(expr, start_time=None, ret_type=None, day_or=True,
hash_id=None, second_at_beginning=False)| Method | Description |
|---|---|
get_next(ret_type) |
Get next occurrence |
get_prev(ret_type) |
Get previous occurrence |
get_current(ret_type) |
Get current time |
set_current(time) |
Set current time |
all_next(ret_type) |
Iterator for forward iteration |
all_prev(ret_type) |
Iterator for backward iteration |
| Method | Description |
|---|---|
is_valid(expr) |
Validate expression |
match(expr, dt) |
Check if datetime matches |
match_range(expr, start, end) |
Check if match exists in range |
expand(expr) |
Expand expression fields |
┌───────────── minute (0-59)
│ ┌───────────── hour (0-23)
│ │ ┌───────────── day of month (1-31)
│ │ │ ┌───────────── month (1-12 or JAN-DEC)
│ │ │ │ ┌───────────── day of week (0-6 or SUN-SAT)
│ │ │ │ │
* * * * *
- 6-field: Adds seconds (
minute hour day month weekday second, orsecond minute hour day month weekdaywithsecond_at_beginning=True) - 7-field: Adds year (1970–2099)
| Pattern | Example | Description |
|---|---|---|
| Wildcard | * |
Every value |
| Value | 5 |
Specific value |
| Range | 1-5 |
Values 1 through 5 |
| Step | */5 |
Every 5th value |
| List | 1,3,5 |
Values 1, 3, and 5 |
| Last | L |
Last day of month |
| Nth weekday | MON#1 |
First Monday |
| Last weekday | L5 |
Last Friday |
| Hash | H |
Jenkins-style hash (requires hash_id) |
| Random | R |
Random value |
| Placeholder | ? |
Wildcard for day/weekday fields |
| Keyword | Equivalent |
|---|---|
@yearly / @annually |
0 0 1 1 * |
@monthly |
0 0 1 * * |
@weekly |
0 0 * * 0 |
@daily / @midnight |
0 0 * * * |
@hourly |
0 * * * * |
from croniter_rs import croniter_range
from datetime import datetime
start = datetime(2024, 1, 1, 0, 0)
stop = datetime(2024, 1, 1, 1, 0)
for dt in croniter_range(start, stop, "*/15 * * * *"):
print(dt)from croniter_rs import (
MINUTE_FIELD, # 0
HOUR_FIELD, # 1
DAY_FIELD, # 2
MONTH_FIELD, # 3
DOW_FIELD, # 4
SECOND_FIELD, # 5
YEAR_FIELD, # 6
UTC_DT, # datetime.timezone.utc
)All exception types match the original croniter for compatibility:
CroniterError— Base exceptionCroniterBadCronError— Invalid cron expressionCroniterBadDateError— Failed to find matching dateCroniterNotAlphaError— Invalid alphabetic valueCroniterUnsupportedSyntaxError— Unsupported syntaxCroniterBadTypeRangeError— Type mismatch in range
| Feature | Status |
|---|---|
| Timezone-aware datetime (full DST support) | Planned |
This project uses a multi-layered testing strategy:
- 301 Rust unit tests — covering parser, scheduler, and edge cases
- 2.3M differential fuzz tests — comparing output against Python croniter
- 5,200 real-world expressions — sourced from GitHub Actions workflows
- 95%+ code coverage — remaining uncovered lines are unreachable defensive code
git clone https://github.com/Souls-R/croniter-rs.git
cd croniter-rs
pip install maturin pytest croniter
maturin develop
cargo test # Rust tests
pytest tests/ -v # Python integration testsMIT License. See LICENSE.
Thanks to the croniter project and its contributors for the design and specification that this implementation follows.