Cron Schedule Parsing

Overview

The Cron Schedule Parsing module implements a minimal yet reliable parser for standard five-field cron-like expressions. Its primary purpose is to convert human-readable scheduling strings into structured data that can be used to determine specific times when tasks should be executed. This parsing is essential for scheduling utilities that need to interpret and calculate future run times based on familiar cron syntax.

Core Concepts and Purpose

Cron expressions specify schedules using five fields representing:

This module focuses on interpreting these fields in a simplified manner, supporting only a limited syntax subset:

By converting these textual expressions into discrete sets of integers, the module enables downstream components to efficiently check if a given datetime matches the schedule.

Key Functionalities and Workflow

Parsing Individual Fields

The function _expand is responsible for parsing each cron field string into a sorted list of unique integer values. It interprets the simple syntax described above and enforces valid ranges per field type.

Example snippet illustrating this:

def _expand(field: str, *, min_v: int, max_v: int) -> CronField:
    if field == "*":
        values = list(range(min_v, max_v + 1))
    elif "-" in field:
        a, b = field.split("-", 1)
        values = list(range(int(a), int(b) + 1))
    else:
        values = [int(x) for x in field.split(",")]
    return CronField(sorted(set(values)))

This approach balances simplicity with enough expressiveness for many common cron schedules, avoiding the complexity of full cron syntax support.

Parsing the Full Cron Expression

The parse function takes a full cron expression string consisting of five fields separated by spaces. It splits the string and delegates each field to _expand with the appropriate minimum and maximum values for validation and expansion.

Example of parsing a typical expression:

def parse(cron: str):
    m, h, dom, mon, dow = cron.split()
    return (
        _expand(m, min_v=0, max_v=59),
        _expand(h, min_v=0, max_v=23),
        _expand(dom, min_v=1, max_v=31),
        _expand(mon, min_v=1, max_v=12),
        _expand(dow, min_v=0, max_v=6),
    )

The return is a tuple of CronField objects, each containing the list of allowed values for the respective time unit.

Integration with Next Run Calculation

While parsing itself only converts the cron string into structured data, it is integral to the Next Run Calculation module. The next_run function demonstrates how the parsed fields are used to find the closest future datetime matching the schedule.

The interaction workflow is:

  1. Call parse(cron) to get expanded fields.

  2. Iterate minute-by-minute starting just after the reference time.

  3. Check if the current candidate datetime matches all five fields.

  4. Return the first matching datetime found, or error if none found within a year.

This design cleanly separates parsing concerns from scheduling logic, allowing reuse and easy extension.

Module Interactions and Relationships

This modular approach keeps parsing concerns isolated, enabling enhancements like adding more complex syntax support or validation without affecting scheduling computations.

Important Concepts and Design Patterns

Mermaid Diagram: Sequence of Cron Expression Parsing and Scheduling

sequenceDiagram
participant User as User Input
participant Parser as Cron Parser
participant Scheduler as Next Run Calculator
participant Clock as System Clock
User->>Parser: Provide cron expression string
Parser->>Parser: Parse fields with _expand()
Parser-->>Scheduler: Return expanded CronField sets
Scheduler->>Clock: Get current or specified datetime
Scheduler->>Scheduler: Iterate minute-by-minute
Scheduler->>Parser: Check if datetime matches fields
alt Match found
Scheduler-->>User: Return next run datetime
else No match within 1 year
Scheduler-->>User: Raise error
end

This diagram clarifies the interaction flow from receiving a cron expression through parsing and to calculating the next scheduled run time, showing responsibilities and data flow between components.