Croner is a fully-featured, lightweight, and efficient Rust library designed for parsing and evaluating cron patterns.
This is the Rust flavor of the popular JavaScript/TypeScript cron parser croner.
L
for the last day and weekday of the month, # for the nth weekday of the
month, W for closest weekday to a day of month.with_alternative_weekdayschrono, (optionally) chrono-tz, and jiff.jiff: Enables support for jiff::Zoned and jiff::civil::DateTime. Disabled by default.chrono: Enables support for chrono::DateTime and chrono::NaiveDateTime. Enabled by default when default features are left on.serde: Enables serde::Serialize and serde::Deserialize implementations for Cron. This feature is disabled by default.Croner combines the features of cron and saffron, while following the POSIX/Vixie "standards" for the relevant parts. See this table:
| Feature | Croner | Cron | Saffron |
|---|---|---|---|
| Time Zones | X | X | |
| Ranges (15-25) | X | X | X |
| Ranges with stepping (15-25/2) | X | X | X |
L - Last day of month | X | X | |
5#L - Last occurrence of weekday | X | X | |
5L - Last occurrence of weekday | X | ? | X |
# - Nth occurrence of weekday | X | X | |
W - Closest weekday | X | X | |
+ - dom-AND-dow through pattern | X | ||
| "Standards"-compliant weekdays (1 is monday) | X | ||
| Five part patterns (minute granularity) | X | X | |
| Six part patterns (second granularity) | X | X | |
| Weekday/Month text representations | X | X | X |
Aliases (@hourly etc.) | X | X | |
chrono DateTime compatibility | X | X | X |
| Option to force DOM-and-DOW | X | ||
| Generate human readable string | X | X |
[!NOTE] Tests carried out at 2023-12-02 using
cron@0.12.0andsaffron@.0.1.0
Ensure you have Rust installed on your machine. If not, you can get it from the official Rust website.
Add croner to your Cargo.toml dependencies:
[dependencies]
croner = "4.0"
If you want to use jiff instead of the default chrono backend:
[dependencies]
croner = { version = "4.0", default-features = false, features = ["jiff"] }
jiff = "0.2"
See MIGRATION.md for a detailed guide covering all breaking changes, including chrono becoming optional, step syntax validation, generic CronDateTime method signatures, the new jiff backend, and more.
Here's a quick example to get you started with matching current time, and
finding the next occurrence using chrono::Local (the default backend):
use chrono::Local;
use croner::Cron;
use std::str::FromStr as _;
fn main() {
// Parse cron expression
let cron_all = Cron::from_str("18 * * * 5")
.expect("Couldn't parse cron string");
// Compare cron pattern with current local time
let time = Local::now();
let matches_all = cron_all.is_time_matching(&time).unwrap();
// Get next match
let next = cron_all.find_next_occurrence(&time, false).unwrap();
// Output results
println!("Description: {}", cron_all.describe());
println!("Time is: {}", time);
println!("Pattern \"{}\" does {} time {}", cron_all.pattern, if matches_all { "match" } else { "not match" }, time );
println!("Pattern \"{}\" will match next time at {}", cron_all.pattern, next);
}
If you prefer the jiff backend:
use croner::Cron;
use jiff::Zoned;
use std::str::FromStr as _;
fn main() {
// Parse cron expression
let cron_all = Cron::from_str("18 * * * 5")
.expect("Couldn't parse cron string");
// Compare cron pattern with current zoned time
let time = Zoned::now();
let matches_all = cron_all.is_time_matching(&time).unwrap();
// Get next match
let next = cron_all.find_next_occurrence(&time, false).unwrap();
// Output results
println!("Description: {}", cron_all.describe());
println!("Time is: {}", time);
println!("Pattern \"{}\" does {} time {}", cron_all.pattern, if matches_all { "match" } else { "not match" }, time );
println!("Pattern \"{}\" will match next time at {}", cron_all.pattern, next);
}
To match against a non-local timezone with the chrono backend, croner supports
zoned chrono DateTime<Tz>. To use a named time zone, you can utilize the
chrono-tz crate.
use croner::Cron;
use chrono::Local;
use chrono_tz::Tz;
use std::str::FromStr as _;
fn main() {
// Parse cron expression
let cron = Cron::from_str("18 * * * 5")
.expect("Couldn't parse cron string");
// Choose a different time zone, for example America/New_York
let est_timezone: Tz = "America/New_York".parse().expect("Invalid timezone");
// Find the next occurrence in EST
let time_est = Local::now().with_timezone(&est_timezone);
let next_est = cron.find_next_occurrence(&time_est, false).unwrap();
// Output results for EST
println!("EST time is: {}", time_est);
println!(
"Pattern \"{}\" will match next time at (EST): {}",
cron.pattern.to_string(),
next_est
);
}
This example demonstrates how to calculate the next 5 occurrences of New Year's Eve that fall on a Friday. We'll use a cron expression to match every Friday (FRI) in December (12) and configure dom_and_dow to ensure both day-of-month and day-of-week conditions are met (see configuration for more details).
use chrono::Local;
use croner::parser::CronParser;
fn main() {
// Parse cron expression for Fridays in December
let cron = CronParser::builder()
// Include seconds in pattern
.seconds(croner::parser::Seconds::Optional)
// Ensure both day of month and day of week conditions are met
.dom_and_dow(true)
.build()
.parse("0 0 0 31 12 FRI")
.expect("Couldn't parse cron string");
let time = Local::now();
println!("Finding the next 5 New Year's Eves on a Friday:");
for time in cron.iter_from(time).take(5) {
println!("{time}");
}
}
The expressions used by Croner are very similar to those of Vixie Cron, but with a few additions and changes as outlined below:
// ┌──────────────── (optional) second (0 - 59)
// │ ┌────────────── minute (0 - 59)
// │ │ ┌──────────── hour (0 - 23)
// │ │ │ ┌────────── day of month (1 - 31)
// │ │ │ │ ┌──────── month (1 - 12, JAN-DEC)
// │ │ │ │ │ ┌────── day of week (0 - 6, SUN-Mon)
// │ │ │ │ │ │ (0 to 6 are Sunday to Saturday; 7 is Sunday, the same as 0)
// │ │ │ │ │ │
// * * * * * *
*. This allow for legacy cron patterns
to be used.5#L represents the last Friday of the month.5#2 in the day of week field
signifies the second Friday of the month. This can be combined with ranges
and supports day names. For instance, MON-FRI#2 would match the Monday
through Friday of the second week of the month.0 0 1 * +MON will run only
if the 1st of the month is also a Monday.| Field | Required | Allowed values | Allowed special characters | Remarks |
|---|---|---|---|---|
| Seconds | Optional | 0-59 | * , - / | |
| Minutes | Yes | 0-59 | * , - / | |
| Hours | Yes | 0-23 | * , - / | |
| Day of Month | Yes | 1-31 | * , - / ? L W | |
| Month | Yes | 1-12 or JAN-DEC | * , - / | |
| Day of Week | Yes | 0-7 or SUN-MON | * , - / ? # L + | 0 to 6 are Sunday to Saturday 7 is Sunday, the same as 0 # is used to specify nth occurrence of a weekday |
[!NOTE] Weekday and month names are case-insensitive. Both
MONandmonwork. When usingLin the Day of Week field, it affects all specified weekdays. For example,5-6#Lmeans the last Friday and Saturday in the month." The # character can be used to specify the "nth" weekday of the month. For example, 5#2 represents the second Friday of the month.
[!NOTE] The
Wfeature is constrained within the given month. The search for the closest weekday will not cross into a previous or subsequent month. For example, if the 1st of the month is a Saturday, 1W will trigger on Monday the 3rd, not the last Friday of the previous month.
It is also possible to use the following "nicknames" as pattern.
| Nickname | Description |
|---|---|
| @yearly | Run once a year, ie. "0 0 1 1 *". |
| @annually | Run once a year, ie. "0 0 1 1 *". |
| @monthly | Run once a month, ie. "0 0 1 * *". |
| @weekly | Run once a week, ie. "0 0 * * 0". |
| @daily | Run once a day, ie. "0 0 * * *". |
| @hourly | Run once an hour, ie. "0 * * * *". |
Croner uses CronParser to parse the cron expression. Invoking
Cron::from_str("pattern") is equivalent to
CronParser::new().parse("pattern"). You can customise the parser by creating a
parser builder using CronParser::builder.
This option enables the inclusion of seconds in the cron pattern, but it's not mandatory. By using this option, you can create cron patterns that either include or omit the seconds field. This offers greater flexibility, allowing for more precise scheduling without imposing the strict requirement of defining seconds in every pattern.
Example Usage:
use croner::parser::{CronParser, Seconds};
// Configure the parser to allow seconds.
let parser = CronParser::builder().seconds(Seconds::Optional).build();
let cron = parser
.parse("*/10 * * * * *") // Every 10 seconds
.expect("Invalid cron pattern");
In contrast to Seconds::Optional, the Seconds::Required variant requires the seconds field in every cron pattern. This enforces a high level of precision in task scheduling, ensuring that every pattern explicitly specifies the second at which the task should run.
Example Usage:
use croner::parser::{CronParser, Seconds};
// Configure the parser to require seconds.
let parser = CronParser::builder().seconds(Seconds::Required).build();
let cron = parser
.parse("5 */2 * * * *") // At 5 seconds past every 2 minutes
.expect("Invalid cron pattern");
dom_and_dowThis method forces the combination of Day of Month (DOM) and Day of Week (DOW) conditions in your cron expressions. It's particularly useful for creating schedules that require specificity in terms of both the day of the month and the day of the week, such as running a task when the first of the month is a Monday, or christmas day is on a friday. Certain libraries use this mode by default.
[!NOTE] While this method provides a way to globally enforce AND logic, the recommended approach is to use the
+modifier directly in the cron pattern (e.g.,0 0 1 * +MON). This pattern-level configuration gives you more granular control and is enabled by default.
Example Usage:
use croner::parser::CronParser;
// Configure the parser to enable DOM and DOW.
let parser = CronParser::builder().dom_and_dow(true).build();
let cron = parser
.parse("0 0 25 * FRI") // When christmas day is on a friday
.expect("Invalid cron pattern");
alternative_weekdays (Quartz mode)This configuration method switches the weekday mode from the POSIX standard to the Quartz-style, commonly used in Java-based scheduling systems. It's useful for those who are accustomed to Quartz's way of specifying weekdays or for ensuring compatibility with existing Quartz-based schedules.
Example Usage:
use croner::parser::CronParser;
// Configure the parser to use Quartz-style weekday mode.
let parser = CronParser::builder().alternative_weekdays(true).build();
let cron = parser
.parse("0 0 12 * * 6") // Every Friday (denoted with 6 in Quartz mode) at noon
.expect("Invalid cron pattern");
sloppy_rangesThis option enables backward compatibility with non-standard step syntax. When enabled, patterns like 0/10 (start at 0, step by 10) and /10 (same as */10) are accepted. These patterns are not compliant with OCPS/vixie-cron standards, which only allow */Z (wildcard with step) and X-Y/Z (range with step).
Note: This option is disabled by default to ensure compliance with cron standards. Only enable it if you need to support legacy cron patterns.
Example Usage:
use croner::parser::CronParser;
// Configure the parser to allow sloppy range syntax.
let parser = CronParser::builder().sloppy_ranges(true).build();
let cron = parser
.parse("0/10 * * * *") // Start at 0, step by 10 (0, 10, 20, 30, 40, 50)
.expect("Valid with sloppy_ranges enabled");
let cron2 = parser
.parse("/10 * * * *") // Same as */10 (0, 10, 20, 30, 40, 50)
.expect("Valid with sloppy_ranges enabled");
If you're migrating from Quartz or need croner-rust to behave like the Quartz scheduler, you can configure the parser to match Quartz's behavior. The main differences between Quartz and standard POSIX cron are:
0/10Example Usage:
use croner::parser::{CronParser, Seconds};
// Configure the parser for Quartz compatibility.
let parser = CronParser::builder()
.alternative_weekdays(true) // Use Quartz-style weekday numbering (1=SUN, 7=SAT)
.seconds(Seconds::Required) // Require seconds field (6 or 7 fields)
.sloppy_ranges(true) // Allow non-standard step syntax like 0/10
.build();
// Parse a Quartz-style cron expression: every Friday at noon
let cron = parser
.parse("0 0 12 ? * 6") // Quartz format: sec min hour dom month dow
.expect("Invalid cron pattern");
For detailed usage and API documentation, visit Croner on docs.rs.
Croner uses the chrono crate, which operates on a proleptic Gregorian calendar. This means it treats all dates, historical or future, as if the Gregorian calendar has always been in effect. Consequently, it does not account for historical calendar reforms (e.g., skipped days during the 1582 Gregorian adoption) and will iterate through all dates uniformly.
For stability and practical use, Croner supports dates from year 1 AD/CE up to the beginning of year 5000, preventing searches that are too far into the past or future.
Croner-rust provides robust and predictable handling of Daylight Saving Time (DST) transitions, aligning with the Open Cron Pattern Specification (OCPS) and Vixie-cron's time-tested behavior. Jobs are categorized based on their time-unit field specifications:
0 30 2 * * *).*) or step values (*/N) in their seconds, minutes, or hours fields (e.g., */5 * * * * *).During DST transitions, Croner-rust behaves as follows:
Searches and iterators always move in real time, not in wall clock time. Over a duplicated hour they run through the whole first pass before the whole second pass, so a forward search never returns a time in the past and a backward search never returns one in the future. The start time you give decides which pass a search is on, so pass the exact value the previous call returned.
To start developing in the Croner project:
cargo build.cargo test --all-features.cargo run --example simple_democargo run --example simple_demo_jiff --no-default-features --features jiffWe welcome contributions! Please feel free to submit a pull request or open an issue.
This project is licensed under the MIT License - see the LICENSE.md file for details.
Please note that Croner is currently in its early stages of development. As such, the API is subject to change in future releases, adhering to semantic versioning principles. We recommend keeping this in mind when integrating Croner into your projects.
If you have any questions or feedback, please open an issue in the repository and we'll get back to you as soon as possible.
512 followers · starred Sep 2025
10 followers · starred Aug 2024
Croner is a fully-featured, lightweight, and efficient Rust library designed for parsing and evaluating cron patterns.
This is the Rust flavor of the popular JavaScript/TypeScript cron parser croner.
L
for the last day and weekday of the month, # for the nth weekday of the
month, W for closest weekday to a day of month.with_alternative_weekdayschrono, (optionally) chrono-tz, and jiff.jiff: Enables support for jiff::Zoned and jiff::civil::DateTime. Disabled by default.chrono: Enables support for chrono::DateTime and chrono::NaiveDateTime. Enabled by default when default features are left on.serde: Enables serde::Serialize and serde::Deserialize implementations for Cron. This feature is disabled by default.Croner combines the features of cron and saffron, while following the POSIX/Vixie "standards" for the relevant parts. See this table:
| Feature | Croner | Cron | Saffron |
|---|---|---|---|
| Time Zones | X | X | |
| Ranges (15-25) | X | X | X |
| Ranges with stepping (15-25/2) | X | X | X |
L - Last day of month | X | X | |
5#L - Last occurrence of weekday | X | X | |
5L - Last occurrence of weekday | X | ? | X |
# - Nth occurrence of weekday | X | X | |
W - Closest weekday | X | X | |
+ - dom-AND-dow through pattern | X | ||
| "Standards"-compliant weekdays (1 is monday) | X | ||
| Five part patterns (minute granularity) | X | X | |
| Six part patterns (second granularity) | X | X | |
| Weekday/Month text representations | X | X | X |
Aliases (@hourly etc.) | X | X | |
chrono DateTime compatibility | X | X | X |
| Option to force DOM-and-DOW | X | ||
| Generate human readable string | X | X |
[!NOTE] Tests carried out at 2023-12-02 using
cron@0.12.0andsaffron@.0.1.0
Ensure you have Rust installed on your machine. If not, you can get it from the official Rust website.
Add croner to your Cargo.toml dependencies:
[dependencies]
croner = "4.0"
If you want to use jiff instead of the default chrono backend:
[dependencies]
croner = { version = "4.0", default-features = false, features = ["jiff"] }
jiff = "0.2"
See MIGRATION.md for a detailed guide covering all breaking changes, including chrono becoming optional, step syntax validation, generic CronDateTime method signatures, the new jiff backend, and more.
Here's a quick example to get you started with matching current time, and
finding the next occurrence using chrono::Local (the default backend):
use chrono::Local;
use croner::Cron;
use std::str::FromStr as _;
fn main() {
// Parse cron expression
let cron_all = Cron::from_str("18 * * * 5")
.expect("Couldn't parse cron string");
// Compare cron pattern with current local time
let time = Local::now();
let matches_all = cron_all.is_time_matching(&time).unwrap();
// Get next match
let next = cron_all.find_next_occurrence(&time, false).unwrap();
// Output results
println!("Description: {}", cron_all.describe());
println!("Time is: {}", time);
println!("Pattern \"{}\" does {} time {}", cron_all.pattern, if matches_all { "match" } else { "not match" }, time );
println!("Pattern \"{}\" will match next time at {}", cron_all.pattern, next);
}
If you prefer the jiff backend:
use croner::Cron;
use jiff::Zoned;
use std::str::FromStr as _;
fn main() {
// Parse cron expression
let cron_all = Cron::from_str("18 * * * 5")
.expect("Couldn't parse cron string");
// Compare cron pattern with current zoned time
let time = Zoned::now();
let matches_all = cron_all.is_time_matching(&time).unwrap();
// Get next match
let next = cron_all.find_next_occurrence(&time, false).unwrap();
// Output results
println!("Description: {}", cron_all.describe());
println!("Time is: {}", time);
println!("Pattern \"{}\" does {} time {}", cron_all.pattern, if matches_all { "match" } else { "not match" }, time );
println!("Pattern \"{}\" will match next time at {}", cron_all.pattern, next);
}
To match against a non-local timezone with the chrono backend, croner supports
zoned chrono DateTime<Tz>. To use a named time zone, you can utilize the
chrono-tz crate.
use croner::Cron;
use chrono::Local;
use chrono_tz::Tz;
use std::str::FromStr as _;
fn main() {
// Parse cron expression
let cron = Cron::from_str("18 * * * 5")
.expect("Couldn't parse cron string");
// Choose a different time zone, for example America/New_York
let est_timezone: Tz = "America/New_York".parse().expect("Invalid timezone");
// Find the next occurrence in EST
let time_est = Local::now().with_timezone(&est_timezone);
let next_est = cron.find_next_occurrence(&time_est, false).unwrap();
// Output results for EST
println!("EST time is: {}", time_est);
println!(
"Pattern \"{}\" will match next time at (EST): {}",
cron.pattern.to_string(),
next_est
);
}
This example demonstrates how to calculate the next 5 occurrences of New Year's Eve that fall on a Friday. We'll use a cron expression to match every Friday (FRI) in December (12) and configure dom_and_dow to ensure both day-of-month and day-of-week conditions are met (see configuration for more details).
use chrono::Local;
use croner::parser::CronParser;
fn main() {
// Parse cron expression for Fridays in December
let cron = CronParser::builder()
// Include seconds in pattern
.seconds(croner::parser::Seconds::Optional)
// Ensure both day of month and day of week conditions are met
.dom_and_dow(true)
.build()
.parse("0 0 0 31 12 FRI")
.expect("Couldn't parse cron string");
let time = Local::now();
println!("Finding the next 5 New Year's Eves on a Friday:");
for time in cron.iter_from(time).take(5) {
println!("{time}");
}
}
The expressions used by Croner are very similar to those of Vixie Cron, but with a few additions and changes as outlined below:
// ┌──────────────── (optional) second (0 - 59)
// │ ┌────────────── minute (0 - 59)
// │ │ ┌──────────── hour (0 - 23)
// │ │ │ ┌────────── day of month (1 - 31)
// │ │ │ │ ┌──────── month (1 - 12, JAN-DEC)
// │ │ │ │ │ ┌────── day of week (0 - 6, SUN-Mon)
// │ │ │ │ │ │ (0 to 6 are Sunday to Saturday; 7 is Sunday, the same as 0)
// │ │ │ │ │ │
// * * * * * *
*. This allow for legacy cron patterns
to be used.5#L represents the last Friday of the month.5#2 in the day of week field
signifies the second Friday of the month. This can be combined with ranges
and supports day names. For instance, MON-FRI#2 would match the Monday
through Friday of the second week of the month.0 0 1 * +MON will run only
if the 1st of the month is also a Monday.| Field | Required | Allowed values | Allowed special characters | Remarks |
|---|---|---|---|---|
| Seconds | Optional | 0-59 | * , - / | |
| Minutes | Yes | 0-59 | * , - / | |
| Hours | Yes | 0-23 | * , - / | |
| Day of Month | Yes | 1-31 | * , - / ? L W | |
| Month | Yes | 1-12 or JAN-DEC | * , - / | |
| Day of Week | Yes | 0-7 or SUN-MON | * , - / ? # L + | 0 to 6 are Sunday to Saturday 7 is Sunday, the same as 0 # is used to specify nth occurrence of a weekday |
[!NOTE] Weekday and month names are case-insensitive. Both
MONandmonwork. When usingLin the Day of Week field, it affects all specified weekdays. For example,5-6#Lmeans the last Friday and Saturday in the month." The # character can be used to specify the "nth" weekday of the month. For example, 5#2 represents the second Friday of the month.
[!NOTE] The
Wfeature is constrained within the given month. The search for the closest weekday will not cross into a previous or subsequent month. For example, if the 1st of the month is a Saturday, 1W will trigger on Monday the 3rd, not the last Friday of the previous month.
It is also possible to use the following "nicknames" as pattern.
| Nickname | Description |
|---|---|
| @yearly | Run once a year, ie. "0 0 1 1 *". |
| @annually | Run once a year, ie. "0 0 1 1 *". |
| @monthly | Run once a month, ie. "0 0 1 * *". |
| @weekly | Run once a week, ie. "0 0 * * 0". |
| @daily | Run once a day, ie. "0 0 * * *". |
| @hourly | Run once an hour, ie. "0 * * * *". |
Croner uses CronParser to parse the cron expression. Invoking
Cron::from_str("pattern") is equivalent to
CronParser::new().parse("pattern"). You can customise the parser by creating a
parser builder using CronParser::builder.
This option enables the inclusion of seconds in the cron pattern, but it's not mandatory. By using this option, you can create cron patterns that either include or omit the seconds field. This offers greater flexibility, allowing for more precise scheduling without imposing the strict requirement of defining seconds in every pattern.
Example Usage:
use croner::parser::{CronParser, Seconds};
// Configure the parser to allow seconds.
let parser = CronParser::builder().seconds(Seconds::Optional).build();
let cron = parser
.parse("*/10 * * * * *") // Every 10 seconds
.expect("Invalid cron pattern");
In contrast to Seconds::Optional, the Seconds::Required variant requires the seconds field in every cron pattern. This enforces a high level of precision in task scheduling, ensuring that every pattern explicitly specifies the second at which the task should run.
Example Usage:
use croner::parser::{CronParser, Seconds};
// Configure the parser to require seconds.
let parser = CronParser::builder().seconds(Seconds::Required).build();
let cron = parser
.parse("5 */2 * * * *") // At 5 seconds past every 2 minutes
.expect("Invalid cron pattern");
dom_and_dowThis method forces the combination of Day of Month (DOM) and Day of Week (DOW) conditions in your cron expressions. It's particularly useful for creating schedules that require specificity in terms of both the day of the month and the day of the week, such as running a task when the first of the month is a Monday, or christmas day is on a friday. Certain libraries use this mode by default.
[!NOTE] While this method provides a way to globally enforce AND logic, the recommended approach is to use the
+modifier directly in the cron pattern (e.g.,0 0 1 * +MON). This pattern-level configuration gives you more granular control and is enabled by default.
Example Usage:
use croner::parser::CronParser;
// Configure the parser to enable DOM and DOW.
let parser = CronParser::builder().dom_and_dow(true).build();
let cron = parser
.parse("0 0 25 * FRI") // When christmas day is on a friday
.expect("Invalid cron pattern");
alternative_weekdays (Quartz mode)This configuration method switches the weekday mode from the POSIX standard to the Quartz-style, commonly used in Java-based scheduling systems. It's useful for those who are accustomed to Quartz's way of specifying weekdays or for ensuring compatibility with existing Quartz-based schedules.
Example Usage:
use croner::parser::CronParser;
// Configure the parser to use Quartz-style weekday mode.
let parser = CronParser::builder().alternative_weekdays(true).build();
let cron = parser
.parse("0 0 12 * * 6") // Every Friday (denoted with 6 in Quartz mode) at noon
.expect("Invalid cron pattern");
sloppy_rangesThis option enables backward compatibility with non-standard step syntax. When enabled, patterns like 0/10 (start at 0, step by 10) and /10 (same as */10) are accepted. These patterns are not compliant with OCPS/vixie-cron standards, which only allow */Z (wildcard with step) and X-Y/Z (range with step).
Note: This option is disabled by default to ensure compliance with cron standards. Only enable it if you need to support legacy cron patterns.
Example Usage:
use croner::parser::CronParser;
// Configure the parser to allow sloppy range syntax.
let parser = CronParser::builder().sloppy_ranges(true).build();
let cron = parser
.parse("0/10 * * * *") // Start at 0, step by 10 (0, 10, 20, 30, 40, 50)
.expect("Valid with sloppy_ranges enabled");
let cron2 = parser
.parse("/10 * * * *") // Same as */10 (0, 10, 20, 30, 40, 50)
.expect("Valid with sloppy_ranges enabled");
If you're migrating from Quartz or need croner-rust to behave like the Quartz scheduler, you can configure the parser to match Quartz's behavior. The main differences between Quartz and standard POSIX cron are:
0/10Example Usage:
use croner::parser::{CronParser, Seconds};
// Configure the parser for Quartz compatibility.
let parser = CronParser::builder()
.alternative_weekdays(true) // Use Quartz-style weekday numbering (1=SUN, 7=SAT)
.seconds(Seconds::Required) // Require seconds field (6 or 7 fields)
.sloppy_ranges(true) // Allow non-standard step syntax like 0/10
.build();
// Parse a Quartz-style cron expression: every Friday at noon
let cron = parser
.parse("0 0 12 ? * 6") // Quartz format: sec min hour dom month dow
.expect("Invalid cron pattern");
For detailed usage and API documentation, visit Croner on docs.rs.
Croner uses the chrono crate, which operates on a proleptic Gregorian calendar. This means it treats all dates, historical or future, as if the Gregorian calendar has always been in effect. Consequently, it does not account for historical calendar reforms (e.g., skipped days during the 1582 Gregorian adoption) and will iterate through all dates uniformly.
For stability and practical use, Croner supports dates from year 1 AD/CE up to the beginning of year 5000, preventing searches that are too far into the past or future.
Croner-rust provides robust and predictable handling of Daylight Saving Time (DST) transitions, aligning with the Open Cron Pattern Specification (OCPS) and Vixie-cron's time-tested behavior. Jobs are categorized based on their time-unit field specifications:
0 30 2 * * *).*) or step values (*/N) in their seconds, minutes, or hours fields (e.g., */5 * * * * *).During DST transitions, Croner-rust behaves as follows:
Searches and iterators always move in real time, not in wall clock time. Over a duplicated hour they run through the whole first pass before the whole second pass, so a forward search never returns a time in the past and a backward search never returns one in the future. The start time you give decides which pass a search is on, so pass the exact value the previous call returned.
To start developing in the Croner project:
cargo build.cargo test --all-features.cargo run --example simple_democargo run --example simple_demo_jiff --no-default-features --features jiffWe welcome contributions! Please feel free to submit a pull request or open an issue.
This project is licensed under the MIT License - see the LICENSE.md file for details.
Please note that Croner is currently in its early stages of development. As such, the API is subject to change in future releases, adhering to semantic versioning principles. We recommend keeping this in mind when integrating Croner into your projects.
If you have any questions or feedback, please open an issue in the repository and we'll get back to you as soon as possible.
512 followers · starred Sep 2025
10 followers · starred Aug 2024