Cron
Utilities for recurring calendar schedules written as cron expressions or
explicit field constraints. A Cron value stores allowed seconds, minutes,
hours, days of month, months, weekdays, and an optional time zone. The module
can create or parse schedules, compare them, test whether a date matches, and
find previous or next scheduled occurrences.
Constructors
Creates a Cron instance from time constraints.
When to use
Use to build a cron schedule from explicit sets of allowed time-field values.
Details
Constructs a cron schedule by specifying which seconds, minutes, hours,
days, months, and weekdays the schedule should match. Empty arrays leave a
time unit unrestricted. If only days or weekdays are restricted, that field
must match. When both are restricted, the default matches either field; set
and: true to require both fields to match. Weekdays range from 0 (Sunday)
to 7 (also Sunday). onDstOverlap chooses which occurrences of repeated
local times are included and defaults to "earlier". The constructor throws a
RangeError when a field contains a non-integer or out-of-range value.
See
- parse for building a schedule from a cron expression string
Signature
declare function make(values: { readonly and?: boolean; readonly days: Iterable<number>; readonly hours: Iterable<number>; readonly minutes: Iterable<number>; readonly months: Iterable<number>; readonly onDstOverlap?: DstOverlapPolicy; readonly seconds?: Iterable<number, any, any>; readonly tz?: TimeZone; readonly weekdays: Iterable<number>;}): CronExample Creating schedules from constraints
import { Cron, DateTime } from "effect"
const utc = DateTime.zoneMakeNamedUnsafe("UTC")
// Every day at midnightconst midnight = Cron.make({ minutes: [0], hours: [0], days: [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31 ], months: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12], weekdays: [0, 1, 2, 3, 4, 5, 6], tz: utc})
// Every 15 minutes during business hours on weekdaysconst businessHours = Cron.make({ minutes: [0, 15, 30, 45], hours: [9, 10, 11, 12, 13, 14, 15, 16, 17], days: [], months: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12], weekdays: [1, 2, 3, 4, 5], // Monday to Friday tz: utc})
Cron.match(midnight, "2024-01-01T00:00:00Z") // => trueCron.match(businessHours, "2024-01-01T09:15:00Z") // => trueParses a cron expression safely into a Cron instance, returning a Result
instead of throwing.
When to use
Use to parse cron expressions from configuration or user input while handling
invalid input as a Result.
Details
The expression may contain five fields, where seconds default to 0, or six
fields including seconds. Fields support *, comma-separated values, ranges,
steps, and month or weekday aliases. Invalid expressions fail with
CronParseError. When both the day-of-month and weekday fields are
restricted, a date matches if either field matches. When either field starts
with *, both fields must match; an unrestricted field always matches.
options.onDstOverlap chooses which occurrences of repeated local times are
included and defaults to "earlier".
See
- parseUnsafe for throwing on invalid cron expressions
- make for constructing a schedule from explicit field constraints
Signature
declare function parse(cron: string, tz?: string | TimeZone, options?: { readonly onDstOverlap?: DstOverlapPolicy;}): Result<Cron, CronParseError>Example Parsing cron expressions
import { Cron, Result } from "effect"
// At 04:00 on every day-of-month from 8 through 14.const cron = Result.getOrThrow(Cron.parse("0 0 4 8-14 * *"))
Array.from(cron.hours) // => [4]Array.from(cron.days) // => [8, 9, 10, 11, 12, 13, 14]Parses a cron expression into a Cron instance, throwing on failure.
When to use
Use when you expect the input to be valid and want to avoid handling the
Result type.
Signature
declare function parseUnsafe(cron: string, tz?: string | TimeZone, options?: { readonly onDstOverlap?: DstOverlapPolicy;}): CronExample Parsing cron expressions unsafely
import { Cron } from "effect"
// At 04:00 on every day-of-month from 8 through 14const cron = Cron.parseUnsafe("0 0 4 8-14 * *", "UTC")
// With timezoneconst cronWithTz = Cron.parseUnsafe("0 0 9 * * *", "America/New_York")
// This would throw an error// const invalid = Cron.parseUnsafe("invalid expression")Cron.match(cron, "2024-01-10T04:00:00Z") // => trueCron.match(cronWithTz, "2024-01-01T14:00:00Z") // => trueErrors
Represents an error that occurs when parsing a cron expression fails.
When to use
Use to handle invalid cron expression failures returned by parse.
Details
This error provides information about what went wrong during parsing, including the error message and optionally the input that caused the error.
See
- parse for the parser that returns this error in
Result.fail - isCronParseError for narrowing unknown values to this error type
Signature
declare class CronParseError extends YieldableError<this> & { readonly _tag: "CronParseError";} & Readonly<{ readonly input?: string; readonly message: string;}> { constructor(args: { readonly input?: string; readonly message: string; }); readonly "~effect/Cron/CronParseError": "~effect/Cron/CronParseError";}Example Handling cron parse failures
import { Cron, Result } from "effect"
const expected = Result.fail(new Cron.CronParseError({ message: "Invalid number of segments in cron expression", input: "invalid expression"}))
Cron.parse("invalid expression") // => expectedGetters
Formats a Cron instance as a cron expression.
Details
The default seconds field (0) is omitted unless includeSeconds is true.
Other seconds configurations are always included.
Gotchas
Formatting drops the timezone information, the onDstOverlap policy, and
the and restriction between days and weekdays. Parsing the result is therefore not guaranteed to produce
an equivalent schedule.
Signature
declare function format(cron: Cron, options?: { readonly includeSeconds?: boolean;}): stringExample Formatting a cron expression
import { Cron } from "effect"
const cron = Cron.parseUnsafe("23 0-20/2 * * 0", "UTC")
Cron.format(cron) // => "23 0-20/2 * * 0"Cron.format(cron, { includeSeconds: true }) // => "0 23 0-20/2 * * 0"Returns the next scheduled date/time for the given Cron instance.
When to use
Use to find the next occurrence of a cron schedule after a specific date/time or after the current time.
Details
Searches for the next date and time when the cron schedule should trigger,
starting after the specified date/time or after the current time when no
date is provided. The result is always strictly after the starting point.
Repeated local times are included according to the schedule's
onDstOverlap policy.
See
Signature
declare function next(cron: Cron, now?: Input): DateExample Finding the next occurrence
import { Cron, Result } from "effect"
const cron = Result.getOrThrow(Cron.parse("0 0 4 8-14 * *", "UTC"))
// Get next run after a specific dateCron.next(cron, "2021-01-01T00:00:00Z").toISOString() // => "2021-01-08T04:00:00.000Z"Returns the previous scheduled date/time for the given Cron instance.
When to use
Use to find the most recent occurrence of a cron schedule before a specific date/time or before the current time.
Details
When no date/time is provided, the search starts from the current time.
Repeated local times are included according to the schedule's
onDstOverlap policy.
Gotchas
The search is strict: if the supplied date/time is exactly an occurrence of the schedule, the result is the earlier occurrence.
See
- next for finding the next scheduled occurrence
Signature
declare function prev(cron: Cron, now?: Input): DateGuards
Checks whether a given value is a Cron instance.
When to use
Use to narrow an unknown value before treating it as a Cron schedule.
Details
This function is a type guard that determines whether the provided value is a valid Cron instance by checking for the presence of the Cron type identifier.
See
Signature
declare function isCron(u: unknown): u is CronExample Checking cron values
import { Cron } from "effect"
const cron = Cron.make({ minutes: [0], hours: [9], days: [1, 15], months: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12], weekdays: [1, 2, 3, 4, 5]})
Cron.isCron(cron) // => trueCron.isCron({}) // => falseCron.isCron("not a cron") // => falseChecks whether a given value is a CronParseError instance.
When to use
Use to narrow an unknown failure before handling it as a cron parse error.
Details
This function is a type guard that determines whether the provided value is a CronParseError by checking for the presence of the CronParseError type identifier.
See
- CronParseError for the parse error type
- parse for producing
CronParseErrorvalues on invalid input
Signature
declare function isCronParseError(u: unknown): u is CronParseErrorExample Checking cron parse errors
import { Cron, Result } from "effect"
Result.mapError(Cron.parse("invalid cron expression"), Cron.isCronParseError) // => Result.fail(true)Cron.isCronParseError(new Error("regular error")) // => falseCron.isCronParseError("not an error") // => falseInstances
Equivalence instance for comparing the timezone, field restrictions, and
day-matching mode of two Cron schedules.
When to use
Use to compare cron schedules through APIs that accept an equivalence relation.
Details
This comparison checks the optional timezone, the and day-matching mode,
the onDstOverlap policy, seconds, minutes, hours, days, months, and
weekdays.
See
- equals for directly comparing two
Cronvalues
Signature
declare const Equivalence: Equ.Equivalence<Cron>Example Comparing schedules with equivalence
import { Cron } from "effect"
const cron1 = Cron.make({ minutes: [0, 30], hours: [9], days: [1, 15], months: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12], weekdays: [1, 2, 3, 4, 5]})
const cron2 = Cron.make({ minutes: [30, 0], // Different order hours: [9], days: [15, 1], // Different order months: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12], weekdays: [1, 2, 3, 4, 5]})
Cron.Equivalence(cron1, cron2) // => trueModels
Represents a cron schedule with time constraints and timezone information.
When to use
Use to represent a recurring calendar schedule that can be matched against dates or used to compute scheduled occurrences.
Details
A Cron instance defines when a scheduled task should run, supporting
seconds, minutes, hours, days, months, and weekday constraints. It also
supports timezone-aware scheduling.
See
Signature
interface Cron extends Pipeable, Equal, Inspectable { readonly "~effect/Cron": "~effect/Cron"; readonly days: ReadonlySet<number>; readonly hours: ReadonlySet<number>; readonly minutes: ReadonlySet<number>; readonly months: ReadonlySet<number>; readonly onDstOverlap: DstOverlapPolicy; readonly seconds: ReadonlySet<number>; readonly tz: Option<TimeZone>; readonly weekdays: ReadonlySet<number>;}Example Creating a cron schedule
import { Cron, DateTime } from "effect"
// Create a cron that runs at 9 AM on weekdaysconst weekdayMorning = Cron.make({ minutes: [0], hours: [9], days: [], months: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12], weekdays: [1, 2, 3, 4, 5], // Monday to Friday tz: DateTime.zoneMakeNamedUnsafe("UTC")})
// Check if a date matches the scheduleCron.match(weekdayMorning, "2023-06-05T09:00:00Z") // => trueDstOverlapPolicy
typeChooses which occurrences of a repeated wall-clock time a Cron schedule
includes.
When to use
Use to control how a schedule behaves when clocks fall back, for example at the end of daylight saving time, and a range of local times occurs twice.
Details
Despite the name, the policy applies to every repeated local time, not only those caused by daylight saving time. For example, Antarctica/Casey repeated 00:00-02:59 on 2023-03-09 when its standard offset changed from +11:00 to +08:00.
"earlier"(default) includes only the first occurrence, before the offset change."later"includes only the second occurrence, after the offset change."both"includes both occurrences, in chronological order.
Time zones without repeated times, such as UTC or fixed offsets, behave the same under every policy.
Gotchas
The policy does not apply to local times skipped when clocks move forward. A
matching time inside such a gap resolves to the instant after the gap, the
same under every policy, and near a gap next and prev are not guaranteed
to return the nearest matching instant.
See
Signature
type DstOverlapPolicy = "earlier" | "later" | "both"Example Running every occurrence of a repeated hour
import { Cron } from "effect"
// Europe/Berlin repeats 02:00-02:59 on 2026-10-25, first at +02:00// (00:30Z) and again at +01:00 (01:30Z)const earlier = Cron.parseUnsafe("30 2 * * *", "Europe/Berlin")const both = Cron.parseUnsafe("30 2 * * *", "Europe/Berlin", { onDstOverlap: "both" })
Cron.next(earlier, "2026-10-25T00:30:00Z").toISOString() // => "2026-10-26T01:30:00.000Z"Cron.next(both, "2026-10-25T00:30:00Z").toISOString() // => "2026-10-25T01:30:00.000Z"Predicates
Checks whether two Cron instances have equal timezone values, field
restrictions, and day-matching modes.
When to use
Use to directly compare two cron schedules, including their timezones and day-matching modes.
Details
The comparison checks the optional timezone, the and day-matching mode,
the onDstOverlap policy, seconds, minutes, hours, days, months, and
weekdays.
See
- Equivalence for the reusable equivalence instance
Signature
declare const equals: { (that: Cron): (self: Cron) => boolean; (self: Cron, that: Cron): boolean;}Example Checking schedule equality
import { Cron } from "effect"
const cron1 = Cron.make({ minutes: [0], hours: [9], days: [1, 15], months: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12], weekdays: [1, 2, 3, 4, 5]})
const cron2 = Cron.make({ minutes: [0], hours: [9], days: [1, 15], months: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12], weekdays: [1, 2, 3, 4, 5]})
Cron.equals(cron1, cron2) // => trueCron.equals(cron1)(cron2) // => trueReturns true when a date/time matches a Cron schedule.
When to use
Use to test whether a specific date/time satisfies a cron schedule.
Details
The schedule's timezone determines which calendar fields are read from the
input; the host system's timezone is used when the schedule has no timezone.
Seconds, minutes, hours, and months are checked against their restrictions;
an empty set leaves that field unrestricted. If only days or weekdays is
restricted, that field must match. If both are restricted, either may match
unless the schedule was created with and: true, which requires both to
match.
See
Signature
declare function match(cron: Cron, date: Input): booleanExample Matching dates against a schedule
import { Cron, Result } from "effect"
const cron = Result.getOrThrow(Cron.parse("0 0 4 8-14 * *", "UTC"))
// Check if specific dates matchCron.match(cron, "2021-01-08T04:00:00Z") // => trueCron.match(cron, "2021-01-08T05:00:00Z") // => falseCron.match(cron, "2021-01-07T04:00:00Z") // => falseSequencing
Returns an infinite iterator that yields dates matching the Cron schedule.
When to use
Use to lazily iterate future occurrences of a cron schedule.
Details
The iterator generates an infinite sequence of dates when the cron schedule should trigger, starting after the specified date/time or after the current time when no date is provided.
See
- next for computing one next occurrence
Signature
declare function sequence(cron: Cron, now?: Input): IterableIterator<Date>Example Iterating scheduled occurrences
import { Cron, Result } from "effect"
const cron = Result.getOrThrow(Cron.parse("0 0 9 * * 1-5", "UTC")) // 9 AM weekdays
// Get first 5 occurrencesconst iterator = Cron.sequence(cron, "2023-01-01T00:00:00Z")const next5 = Array.from({ length: 5 }, () => iterator.next().value.toISOString())const expected = [ "2023-01-02T09:00:00.000Z", "2023-01-03T09:00:00.000Z", "2023-01-04T09:00:00.000Z", "2023-01-05T09:00:00.000Z", "2023-01-06T09:00:00.000Z"]
next5 // => expected