#time #cron #schedule

cron-lingo

Alternative cron-like expression parser for schedulling

10 unstable releases (3 breaking)

0.4.2 Mar 12, 2022
0.4.1 Mar 8, 2022
0.4.0 Dec 4, 2021
0.3.1 Jun 10, 2021
0.1.1 Feb 6, 2021

#10 in #schedule

34 downloads per month

MIT license

56KB
1K SLoC

cron-lingo

A small Rust library to parse a cron-like, human-readable expression like "at 6 AM on Mondays and at 6 PM (Saturdays and Sundays)" and use it to iterate upcoming dates.

The main goal is to provide a more predictable way for e.g. schedulling critical tasks by getting rid of some core functionality of standard cron. Also the expression syntax is self-explanatory to a large extent, which may present a useful side-effect if you are planning to expose the configuration of some scheduler to non-technical staff.

Example

use cron_lingo::Schedule;
use std::str::FromStr;

fn main() {
    let schedule = Schedule::from_str("at 1 PM on Mondays").unwrap();

    for date in schedule.iter().unwrap().take(3) {
        println!("{}", date);
    }
}

// Output:
// 2021-06-14 13:00 +2
// 2021-06-21 13:00 +2
// 2021-06-28 13:00 +2

Please check out the module-level documentation on docs.rs for specifics on the applied syntax.


lib.rs:

This crate allows to parse a cron-like, human-readable expression. The resulting object can be turned into an iterator to compute the next date (as a time::OffsetDateTime) one at a time. By default dates are computed in the current local offset, but the iterator can be configured to use other offsets.

Example

use cron_lingo::Schedule;
use std::str::FromStr;
use time::macros::offset;

fn main() -> Result<(), cron_lingo::error::Error> {
    // Create a schedule from an expression and iterate.
    let expr = "at 6:30 AM on Mondays and Thursdays";
    let schedule1 = Schedule::from_str(expr)?;
    assert!(schedule1.iter()?.next().is_some());

    // Create another schedule, add it to the first, and then iterate.
    let schedule2 = Schedule::from_str("at 8 PM on the first Sunday")?;
    let mut combination = schedule1 + schedule2;
    assert!(combination.iter()?.next().is_some());

    // Finally add another to the existing collection of schedules.
    let schedule3 = Schedule::from_str("at 12:00 PM on the last Friday")?;
    combination += schedule3;
    assert!(combination.iter()?.next().is_some());

    // The examples above assume that the current local offset is to
    // be used to determine the dates, but dates can also be computed
    // in different offsets.
    let expr = "at 6:30 AM on Mondays and Thursdays";
    let schedule = Schedule::from_str(expr)?;
    assert!(schedule.iter()?.assume_offset(offset!(+3)).next().is_some());
    Ok(())
}

Expression syntax

A single expression consists of three parts: a time specification, and optionally a weekday and week specification.

<time spec> [<weekday spec>] [<week spec>]

Here are a few random examples of complete expressions:

  • at 1 AM
  • 6 PM on Saturdays and Sundays
  • at 2 PM (Mondays, Thursdays) in even weeks
  • at 6:45 PM on Wednesdays in odd weeks
  • at 6:30 AM on Mondays
  • at 6 AM, 6 PM (Mondays)
  • at 8 AM on the first Sunday

This table gives some more examples for each type of specification in a block:

Times Weekday (optional) Week (optional)
at every full hour on Mondays and Tuesdays in odd weeks
at 7:30 AM and 7:30 PM on Tuesdays, Saturdays in even weeks
at 6 AM, 6 PM and 8 PM on Fridays
at 6 AM, 12 AM, 6 PM
at 8:30 AM in odd weeks
at 8 AM on Wednesdays
at 08 AM on the first Monday
at 08 PM on the 4th Friday in even weeks
at 08 PM on Wednesdays and Sundays
at 5:45 AM (Mondays and Thursdays)
at 6 AM and 6 PM (first Sunday)
at 1:15 PM (1st Monday and 2nd Friday)
at 1 PM on the third Monday
at 1:50 PM on the 3rd Monday
at 1 PM on the 4th Saturday
at 6 PM on the last Monday

Ruleset

The examples above cover the basic rules of the expression syntax to a certain (and for most use cases probably sufficient) extent. Nevertheless, here is a list of "rules" that an expression must comply with and that you might find useful to avoid mistakes:

Times specification

  • must start with at
  • then follows either every full hour OR a list of distinct times
  • a time adheres to the 12-hour-clock, so a number from 1 to 12 followed by AM or PM (uppercase!), e.g. 1 AM or 1 PM
  • a time may also contain minutes from 00 to 59 (separated from the hour by a colon). Omitting the minutes means on the hour, e.g. 8 PM == 8:00 PM
  • distinct times are concatenated by commata or and

Weekday specification

  • is optional
  • succeeds the time spec
  • consists of a list of weekdays with optional modifiers to select only specific weekdays in a month.
  • the list either starts with on OR is enclosed by simple braces () for compactness
  • a weekday must be one of [ Monday | Tuesday | Wednesday | Thursday | Friday | Saturday | Sunday ] appended with an s if e.g. every Monday is to be included OR a weekday preceded by a modifier [ first | 1st | second | 2nd | third | 3rd | fourth | 4th | last ] in order to include only specific weekdays in a month.

Week specification

  • is optional
  • must be one of in even weeks / in odd weeks

Dependencies

~480KB