cron-parser-py
Cron expression parser with human-readable descriptions and next-run calculation.
Pure Python · Zero dependencies · Python 3.9+
from cron_parser_py import CronParser
expr = CronParser.parse("0 9 * * MON-FRI")
print(expr.description()) # At 9:00 AM, Monday through Friday
print(expr.next_run()) # 2024-01-15 09:00:00
print(expr.next_runs(3)) # [datetime, datetime, datetime]
Installation
pip install cron-parser-py
Features
| Capability | Details |
|---|---|
| 5-field standard cron | MIN HOUR DOM MON DOW |
| 6-field (with seconds) | SEC MIN HOUR DOM MON DOW |
| 7-field (with year) | SEC MIN HOUR DOM MON DOW YEAR |
| Predefined macros | @yearly, @monthly, @weekly, @daily, @hourly, @reboot |
| Special characters | * / , - ? L W # |
| Name aliases | JAN–DEC, SUN–SAT (case-insensitive) |
| Human descriptions | Natural English for any expression |
| Next / previous run | Forward and backward time search |
is_due check |
Determine if an expression fires right now |
| Validation | Detailed error messages with field/value context |
Quick start
Module-level helpers
import cron_parser_py as cron
# Human-readable description
cron.describe("*/5 * * * *") # "Every 5 minutes"
cron.describe("0 9 * * MON-FRI") # "At 9:00 AM, Monday through Friday"
cron.describe("0 0 1 1 *") # "At midnight, on the 1st, in January"
# Validate without raising
cron.is_valid("0 9 * * MON-FRI") # True
cron.is_valid("99 9 * * *") # False
# Next run time
cron.next_run("0 * * * *") # next top-of-hour datetime
# Multiple next runs
cron.next_runs("*/15 * * * *", 5) # list of 5 datetimes
# Previous run
cron.previous_run("0 0 * * *") # most recent midnight
# Is due right now?
cron.is_due("* * * * *") # True (every minute)
CronExpression object
from cron_parser_py import CronParser
expr = CronParser.parse("30 8 * * 1-5")
expr.description() # "At 8:30 AM, Monday through Friday"
expr.next_run() # next scheduled datetime
expr.next_runs(count=10) # list of next 10 datetimes
expr.previous_run() # most recent past run
expr.is_due() # True/False right now
str(expr) # "30 8 * * 1-5"
Supported syntax
Field positions
Standard 5-field:
┌──────── minute (0–59)
│ ┌────── hour (0–23)
│ │ ┌──── day-of-month (1–31)
│ │ │ ┌── month (1–12)
│ │ │ │ ┌ day-of-week (0–7, 0 and 7 = Sunday)
* * * * *
6-field (add seconds at the front):
┌──────────── second (0–59)
│ ┌────────── minute (0–59)
│ │ ┌──────── hour (0–23)
│ │ │ ┌────── day-of-month (1–31)
│ │ │ │ ┌──── month (1–12)
│ │ │ │ │ ┌── day-of-week (0–6)
* * * * * *
Special characters
| Character | Meaning | Example |
|---|---|---|
* |
every value | * * * * * — every minute |
? |
any (day fields only) | 0 0 ? * 1 — every Monday |
/ |
step | */15 * * * * — every 15 min |
- |
range | MON-FRI |
, |
list | 1,15 * * * — 1st and 15th |
L |
last | L in DOM — last day of month |
W |
nearest weekday | 15W — nearest weekday to 15th |
LW |
last weekday | last weekday of month |
# |
Nth weekday | 5#2 — second Friday |
nL |
last Nth weekday | 5L — last Friday |
Month and weekday names
JAN FEB MAR APR MAY JUN JUL AUG SEP OCT NOV DEC
SUN MON TUE WED THU FRI SAT
Names are case-insensitive and can be used in ranges: MON-FRI.
Predefined macros
| Macro | Equivalent | Description |
|---|---|---|
@yearly / @annually |
0 0 1 1 * |
Once a year, on Jan 1st at midnight |
@monthly |
0 0 1 * * |
Once a month, on the 1st at midnight |
@weekly |
0 0 * * 0 |
Once a week, on Sunday at midnight |
@daily / @midnight |
0 0 * * * |
Every day at midnight |
@hourly |
0 * * * * |
Every hour |
@reboot |
— | At system startup |
API Reference
CronParser
CronParser.parse(expression: str) -> CronExpression
Parse a cron expression string. Raises CronParseError on failure.
CronParser.is_valid(expression: str) -> bool
Return True if the expression is parseable without errors.
CronExpression
| Method | Returns | Description |
|---|---|---|
.description() |
str |
Human-readable English description |
.next_run(from_time=None) |
datetime |
Next run after from_time (default: now) |
.next_runs(count=5, from_time=None) |
list[datetime] |
Next N run times |
.previous_run(from_time=None) |
datetime |
Most recent run before from_time |
.is_due(at_time=None, tolerance_seconds=0) |
bool |
True if expression fires now |
str(expr) |
str |
Original expression string |
CronScheduler
from cron_parser_py import CronParser, CronScheduler
expr = CronParser.parse("0 9 * * *")
CronScheduler.next_run(expr)
CronScheduler.next_runs(expr, count=10)
CronScheduler.previous_run(expr)
CronScheduler.is_due(expr, tolerance_seconds=30)
CronScheduler.time_until_next_run(expr) # → timedelta
CronValidator
from cron_parser_py import CronParser, CronValidator
expr = CronParser.parse("0 0 30 2 *")
errors = CronValidator.validate(expr, strict=True)
# ["The combination of month(s) and day(s) can never produce a valid date ..."]
CronValidator.assert_valid(expr, strict=True) # raises CronValidationError
warnings = CronValidator.get_warnings(expr)
CronDescriptor
from cron_parser_py import CronParser, CronDescriptor
expr = CronParser.parse("0 9 * * MON-FRI")
CronDescriptor.describe(expr) # "At 9:00 AM, Monday through Friday"
# Describe a single field
from cron_parser_py import FieldType
from cron_parser_py.parser import parse_field
field = parse_field("MON-FRI", FieldType.DAY_OF_WEEK)
CronDescriptor.describe_field(field) # "Monday through Friday"
Exceptions
from cron_parser_py import (
CronParseError, # base class
CronValidationError, # invalid expression structure
CronFieldError, # invalid value in a specific field
CronScheduleError, # cannot calculate run time
)
Examples
from datetime import datetime
import cron_parser_py as cron
# -- Descriptions --
cron.describe("* * * * *") # "Every minute"
cron.describe("*/5 * * * *") # "Every 5 minutes"
cron.describe("0 * * * *") # "Every hour"
cron.describe("0 */2 * * *") # "Every 2 hours"
cron.describe("0 9 * * *") # "At 9:00 AM"
cron.describe("30 14 * * *") # "At 2:30 PM"
cron.describe("0 9,17 * * *") # "At 9:00 AM and 5:00 PM"
cron.describe("0 9 * * 1-5") # "At 9:00 AM, Monday through Friday"
cron.describe("0 9 * * 1,3,5") # "At 9:00 AM, on Monday, Wednesday, and Friday"
cron.describe("0 0 1 * *") # "At midnight, on the 1st"
cron.describe("0 0 L * *") # "At midnight, on the last day of the month"
cron.describe("0 0 ? * 5#2") # "At midnight, on the second Friday of the month"
cron.describe("0 0 1 1 *") # "At midnight, on the 1st, in January"
cron.describe("@weekly") # "Weekly, at midnight on Sunday"
# -- Scheduling --
ref = datetime(2024, 6, 10, 8, 0) # Monday 08:00
nxt = cron.next_run("0 9 * * MON-FRI", ref)
# datetime(2024, 6, 10, 9, 0) — same day at 09:00
runs = cron.next_runs("*/30 8-18 * * 1-5", count=4, from_time=ref)
# Four 30-minute slots on weekdays between 08:00 and 18:00
prev = cron.previous_run("0 0 * * *", ref)
# datetime(2024, 6, 10, 0, 0) — midnight that day
td = cron.parse("0 9 * * *").next_run(ref) - ref
print(td) # 1:00:00 (1 hour)
License
MIT © Vladyslav Zaiets
Metadata
Release files for cron-parser-py 1.0.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 | |
|---|---|---|---|
| cron_parser_py-1.0.0.tar.gz | 17.1 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| cron_parser_py-1.0.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 38.6 kB
Release files / cron_parser_py-1.0.0.tar.gz
| Download URL | cron_parser_py-1.0.0.tar.gz |
|---|---|
| Size | 17.1 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
f6627618cb0588be2b0e7ad3b4739b3473bc54ae799a7b2fc2215c718e8b84ec
|
|
BLAKE2b-256 checksum How to use checksums |
39741603da497dd7bc3cfedff7e98e70f1d515b30609eaac826526ab95ed9cb0
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.12.3
|
Release files / cron_parser_py-1.0.0-py3-none-any.whl
| Download URL | cron_parser_py-1.0.0-py3-none-any.whl |
|---|---|
| Size | 21.6 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
583443bca6cb2d38b2eed07e9c34da7bea55f44de50e9eb0f5e4d83eeb3e2121
|
|
BLAKE2b-256 checksum How to use checksums |
2a79534f953ae81a0bc06a1b9d59e42d3b37de5f626e5665a8804e473186fe1c
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.12.3
|