3.4 KiB
parseCron
(no submodules)
Purpose
parseCron is the top-level orchestrator that consumes a raw cron
expression string and returns a frozen CronExpression whose nextRun,
nextRuns, previousRun, and matches methods drive every scheduling
query. The orchestrator macro-expands the input first, then dispatches
the resulting tokens through parseField per position, applies optional
Vixie strict-DoW semantics, and finally binds the result to the search
engine in next-run.ts. isValidCron is the non-throwing sibling: it
returns a boolean and guarantees no exception escape for any string
input, as mandated by the spec.
Features
- 5-field default; 6-field opt-in via
seconds: true; 7-field Quartz opt-in via bothsecondsandyearstrue. - Macro expansion (
@daily,@hourly, ...) handled before tokenization. strictDoWoption enforces "either dom or dow, never both" Vixie semantics.nextRuns(count)produces a strictly monotonic array.matches(when, tz)is a constant-time membership check against the parsed field sets.
Usage: Basic Example
import { parseCron, isValidCron } from "@suaveplan/cron";
const expr = parseCron("*/15 * * * *");
console.log(expr.fields.minutes); // [0, 15, 30, 45]
console.log(expr.nextRun(Date.UTC(2026, 4, 11, 10, 7, 0), "UTC"));
// Date.UTC(2026, 4, 11, 10, 15, 0)
console.log(isValidCron("not a cron expression")); // false
console.log(isValidCron("0 9 * * MON-FRI")); // true
Usage: Advanced Example
import { parseCron } from "@suaveplan/cron";
const quartz = parseCron("0 0 12 * * MON-FRI 2026-2030", {
seconds: true,
years: true,
});
const runs = quartz.nextRuns(3, Date.UTC(2026, 0, 1), "America/New_York");
console.log(runs.map((t) => new Date(t).toISOString()));
const strict = parseCron("0 12 15 * 1", { strictDoW: true });
// Rejects expressions where dom AND dow are both restricted.
console.log(strict.matches(Date.UTC(2026, 0, 15, 12, 0, 0), "UTC"));
API Reference
parseCron(expr: string, options?: ParseOptions): CronExpression— parses and returns the frozen expression handle.isValidCron(expr: string, options?: ParseOptions): boolean— never throws.interface CronExpression { expr; fields; nextRun; nextRuns; previousRun; matches }.interface ParseOptions { seconds?; years?; strictDoW? }.interface CronFields { seconds?; minutes; hours; daysOfMonth; months; daysOfWeek; years? }.
Implementation Notes
The orchestrator first invokes expandMacro so the rest of the pipeline
only ever sees canonical 5/6/7-field strings. Tokenization is whitespace-
based with the field count strictly validated against ParseOptions.
Each field is independently parsed via the shared parseField
tokenizer, which returns a sorted, deduplicated readonly number[].
strictDoW enforcement runs after tokenization so the error context
carries the actual parsed sets for debug visibility. The returned
CronExpression is Object.freezed and binds its methods over the
captured fields, so destructuring (const { nextRun } = parseCron(...))
is safe. isValidCron is a tight try/catch around the same code path so
the two functions stay in lock-step with no semantic drift. All parse
calls flow through recordParse so an outcome attribute lands on every
invocation regardless of success or failure.