Skip to content

Souls-R/croniter-rs

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

65 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

croniter-rs

CI PyPI version License: MIT

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.

Why croniter-rs?

  • 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

Installation

pip install croniter-rs

Quick Migration

# 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

Performance

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.

Usage

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)))  # True

API Reference

Constructor

croniter(expr, start_time=None, ret_type=None, day_or=True,
         hash_id=None, second_at_beginning=False)

Instance Methods

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

Class Methods

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

Supported Syntax

Standard Cron (5 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)
│ │ │ │ │
* * * * *

Extended Formats

  • 6-field: Adds seconds (minute hour day month weekday second, or second minute hour day month weekday with second_at_beginning=True)
  • 7-field: Adds year (1970–2099)

Expressions

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 Expressions

Keyword Equivalent
@yearly / @annually 0 0 1 1 *
@monthly 0 0 1 * *
@weekly 0 0 * * 0
@daily / @midnight 0 0 * * *
@hourly 0 * * * *

croniter_range()

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)

Module Constants

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
)

Exception Types

All exception types match the original croniter for compatibility:

  • CroniterError — Base exception
  • CroniterBadCronError — Invalid cron expression
  • CroniterBadDateError — Failed to find matching date
  • CroniterNotAlphaError — Invalid alphabetic value
  • CroniterUnsupportedSyntaxError — Unsupported syntax
  • CroniterBadTypeRangeError — Type mismatch in range

Not Yet Implemented

Feature Status
Timezone-aware datetime (full DST support) Planned

Testing

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

Contributing

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 tests

License

MIT License. See LICENSE.

Acknowledgments

Thanks to the croniter project and its contributors for the design and specification that this implementation follows.

About

No description, website, or topics provided.

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors