cron

repository·master·Indexed 19 days ago

https://github.com/zslayton/cron

A cron expression parser and schedule explorer for Rust (v0.17.0). It allows developers to parse complex cron strings—supporting seconds, minutes, hours, day of month, month, day of week, and year—and calculate future or previous scheduled occurrences using the Schedule object and its associated iterators. The library integrates with the chrono crate for timezone management and correctly handles Daylight Saving Time (DST) transitions.

Tokens
3.4K
Snippets
11
Records
14
Agent score
65%

What's inside cron

  1. Handle Daylight Saving Time (DST) transitions with Schedule

    master

    The Schedule iterator correctly handles ambiguous or non-existent times caused by Daylight Saving Time transitions. When using after(&dt) or nth_back(n), the library ensures that it does not panic when encountering times that fall within a DST gap or overlap.

    For example, in a timezone like America/Chicago, an hourly schedule will correctly yield both instances of an hour that repeats during a fallback transition (e.g., 1 AM CDT followed by 1 AM CST).

    use chrono_tz::Tz;
    use cron::Schedule;
    
    let schedule_tz: Tz = "America/Chicago".parse().unwrap();
    let dt = schedule_tz.with_ymd_and_hms(2022, 11, 5, 23, 30, 0).unwrap();
    let schedule = Schedule::from_str("0 0 * * * * *").unwrap();
    
    // Iterating forward through a DST transition
    let times = schedule
        .after(&dt)
        .map(|x| x.to_string())
        .take(5)
        .collect::<Vec<_>>();
    
    // Expected behavior includes the repeated hour during fallback
    // ["2022-11-06 00:00:00 CDT", "2022-11-06 01:00:00 CDT", "2022-11-06 01:00:00 CST", ...]
  2. Iterate through cron occurrences with Schedule

    master

    The Schedule struct allows you to find upcoming or previous occurrences of a cron expression. You can iterate through these occurrences using ScheduleIterator (which borrows the schedule) or OwnedScheduleIterator (which takes ownership of the schedule).

    Key Methods

    • upcoming<Z>(&self, timezone: Z) -> ScheduleIterator<'_, Z>: Returns an iterator starting with the next occurrence relative to the current time in the specified timezone.
    • after<Z>(&self, after: &DateTime<Z>) -> ScheduleIterator<'_, Z>: Returns an iterator starting with the first occurrence strictly after the provided after timestamp.
    • after_owned<Z>(&self, after: DateTime<Z>) -> OwnedScheduleIterator<Z>: Similar to after, but returns an iterator that owns the Schedule instance.
    • upcoming_owned<Z>(&self, timezone: Z) -> OwnedScheduleIterator<Z>: Similar to upcoming, but returns an iterator that owns the Schedule instance.

    Both iterator types implement Iterator and DoubleEndedIterator, allowing you to use .next() to go forward in time or .next_back() to go backward in time.

    use chrono::{Utc, TimeZone};
    use std::str::FromStr;
    
    let expression = "0 0,30 0,6,12,18 1,15 Jan-March Thurs";
    let schedule = Schedule::from_str(expression).unwrap();
    
    // Get upcoming occurrences in UTC
    let mut upcoming = schedule.upcoming(Utc);
    while let Some(occurrence) = upcoming.next() {
        println!("Next occurrence: {}", occurrence);
    }
    
    // Iterate backwards from a specific point
    let starting_point = Utc.with_ymd_and_hms(2023, 1, 1, 0, 0, 0).unwrap();
    let mut backward = schedule.after(&starting_point).rev();
    while let Some(prev) = backward.next() {
        println!("Previous occurrence: {}", prev);
    }
  3. Parse cron expressions and find upcoming occurrences

    master

    Use the Schedule::from_str method to parse a cron expression string into a Schedule object. Once parsed, you can use the .upcoming(base_time) method to generate an iterator of future DateTime occurrences. This method requires a base time (e.g., from the chrono crate) to start the search for upcoming matches.

    The supported cron expression format includes: sec min hour day of month month day of week year.

    use cron::Schedule;
    use chrono::Utc;
    use std::str::FromStr;
    
    fn main() {
      //               sec  min   hour   day of month   month   day of week   year
      let expression = "0   30   9,12,15     1,15       May-Aug  Mon,Wed,Fri  2018/2";
      let schedule = Schedule::from_str(expression).unwrap();
      println!("Upcoming fire times:");
      for datetime in schedule.upcoming(Utc).take(10) {
        println!("-> {}", datetime);
      }
    }
  4. Initialize a NextAfterQuery to find upcoming occurrences

    master

    Use NextAfterQuery::from(&DateTime<Z>) to create a query object used for finding the next scheduled occurrence after a specific point in time. The query tracks the initial datetime and manages lower bounds for time units (month, day, hour, minute, second) to ensure the search progresses forward correctly.

    Key methods for navigating the search space include:

    • year_lower_bound(): Returns the year of the initial datetime.
    • month_lower_bound(): Returns the current month or the minimum possible month.
    • day_of_month_lower_bound(): Returns the current day or the minimum possible day.
    • hour_lower_bound(), minute_lower_bound(), second_lower_bound(): Return the current unit value or the minimum possible value.
    • reset_month(), reset_day_of_month(), etc.: Resets the search bounds for a specific unit and its sub-units (e.g., resetting the month also resets the day, hour, minute, and second).
    use chrono::TimeZone;
    use cron::NextAfterQuery;
    
    // Assuming 'dt' is a valid DateTime<Z>
    let query = NextAfterQuery::from(&dt);
    let year = query.year_lower_bound();
    let month = query.month_lower_bound();
  5. Compare schedule time unit specifications with `timeunitspec_eq`

    master

    The timeunitspec_eq method allows you to check if two Schedule objects are equivalent in terms of their time unit specifications, even if they are not strictly equal as objects. This is useful for determining if different cron expressions (like @weekly vs a specific weekly cron string) represent the same temporal pattern.

    let schedule_1 = Schedule::from_str("@weekly").unwrap();
    let schedule_2 = Schedule::from_str("0 0 0 * * 1 *").unwrap();
    
    // Returns true if the underlying time unit patterns are equivalent
    assert!(schedule_1.timeunitspec_eq(&schedule_2));
  6. Convert Schedule to String or display it

    master

    A Schedule can be converted back to its original cron expression string using String::from(schedule) or by using the Display trait (e.g., via format!("{}", schedule)).

    let schedule = Schedule::from_str("@hourly").unwrap();
    let source_str = String::from(schedule);
    println!("Cron expression: {}", schedule);
  7. Initialize a PrevFromQuery to find previous occurrences

    master

    Use PrevFromQuery::from(&DateTime<Z>) to create a query object used for finding the previous scheduled occurrence relative to a specific point in time. If the provided datetime has sub-second precision, the query starts from that exact time; otherwise, it starts from one second prior.

    Key methods for navigating the search space include:

    • year_upper_bound(): Returns the year of the initial datetime.
    • month_upper_bound(): Returns the current month or the maximum possible month.
    • day_of_month_upper_bound(): Returns the current day or the maximum possible day.
    • hour_upper_bound(), minute_upper_bound(), second_upper_bound(): Return the current unit value or the maximum possible value.
    • reset_month(), reset_day_of_month(), etc.: Resets the search bounds for a specific unit and its sub-units (e.g., resetting the month also resets the day, hour, minute, and second).
    use chrono::TimeZone;
    use cron::PrevFromQuery;
    
    // Assuming 'dt' is a valid DateTime<Z>
    let query = PrevFromQuery::from(&dt);
    let year = query.year_upper_bound();
    let month = query.month_upper_bound();
  8. Check if a DateTime matches a Schedule

    master

    Use the includes<Z>(&self, date_time: DateTime<Z>) -> bool method to determine if a specific DateTime matches the pattern defined by the Schedule.

    use chrono::{DateTime, Utc};
    
    let schedule = Schedule::from_str("0 0 * * * *").unwrap();
    let dt = Utc.with_ymd_and_hms(2023, 1, 1, 0, 0, 0).unwrap();
    
    if schedule.includes(dt) {
        println!("Match found!");
    }
  9. Parse a cron expression with Schedule::from_str

    master

    Use Schedule::from_str to parse a cron expression string into a Schedule object. The expression can include seconds, minutes, hours, day of month, month, day of week, and year. The expression follows a standard cron format where fields are separated by whitespace.

    Supported fields (in order): sec min hour day_of_month month day_of_week year

    use cron::Schedule;
    use chrono::Utc;
    use std::str::FromStr;
    
    fn main() {
      //               sec  min   hour   day of month   month   day of week   year
      let expression = "0   30   9,12,15     1,15       May-Aug  Mon,Wed,Fri  2018/2";
      let schedule = Schedule::from_str(expression).unwrap();
      println!("Upcoming fire times:");
      for datetime in schedule.upcoming(Utc).take(10) {
        println!("-> {}", datetime);
      }
    }
  10. Access Schedule time unit specifications

    master
    You can inspect the individual components of a Schedule (years, months, days of month, days of week, hours, minutes, seconds) by calling their respective getter methods. These return types implementing TimeUnitSpec.
  11. Iterate over upcoming times with Schedule::upcoming

    master

    Once a Schedule is created, use the upcoming method to generate an iterator of future timestamps. The upcoming method requires a timezone provider (such as chrono::Utc) to determine the next occurrences relative to a specific point in time.

    use cron::Schedule;
    use chrono::Utc;
    use std::str::FromStr;
    
    let expression = "0 30 9 * * * ";
    let schedule = Schedule::from_str(expression).unwrap();
    
    // Returns an iterator of upcoming datetimes
    for datetime in schedule.upcoming(Utc).take(5) {
        println!("Next occurrence: {}", datetime);
    }
  12. Handle cron parsing errors with Error and ErrorKind

    master

    When using the cron crate, errors encountered during expression parsing are returned as an Error struct. This struct wraps an ErrorKind enum. Currently, the only error variant is ErrorKind::Expression(String), which contains the string representation of the failed expression. The Error type implements std::error::Error and std::fmt::Display, making it compatible with standard Rust error handling patterns.

    // The ErrorKind enum defines the specific error categories
    pub enum ErrorKind {
        /// Failed to parse an expression
        Expression(String),
    }
    
    // The Error struct is the primary error type returned by the library
    pub struct Error {
        kind: ErrorKind,
    }