Cron
The Cron module lets you define schedules in a style similar to UNIX cron expressions. It also supports partial constraints (e.g., certain months or weekdays), time zone awareness through the DateTime module, and robust error handling.
This module helps you:
- Create a
Croninstance from individual parts. - Parse and validate cron expressions.
- Match existing dates to see if they satisfy a given cron schedule.
- Find the next occurrence of a schedule after a given date.
- Iterate over future dates that match a schedule.
- Convert a
Croninstance to aSchedulefor use in effectful programs.
Creating a Cron
You can define a cron schedule by specifying numeric constraints for seconds, minutes, hours, days, months, and weekdays. The make function requires you to define all fields representing the schedule’s constraints.
Example (Creating a Cron)
import { Cron, DateTime } from "effect"
// Build a cron that triggers at 4:00 AM// on the 8th to the 14th of each monthconst cron = Cron.make({ seconds: [0], // Trigger at the start of a minute minutes: [0], // Trigger at the start of an hour hours: [4], // Trigger at 4:00 AM days: [8, 9, 10, 11, 12, 13, 14], // Specific days of the month months: [], // No restrictions on the month weekdays: [], // No restrictions on the weekday tz: DateTime.zoneUnsafeMakeNamed("Europe/Rome"), // Optional time zone})seconds,minutes, andhours: Define the time of day.daysandmonths: Specify which calendar days and months are valid.weekdays: Restrict the schedule to specific days of the week.tz: Optionally define the time zone for the schedule.
If any field is left empty (e.g., months), it is treated as having “no constraints,” allowing any valid value for that part of the date.
Parsing Cron Expressions
Instead of manually constructing a Cron, you can use UNIX-like cron strings and parse them with parse or unsafeParse.
parse
The parse(cronExpression, tz?) function safely parses a cron string into a Cron instance. It returns an Either, which will contain either the parsed Cron or a parsing error.
Example (Safely Parsing a Cron Expression)
import { Either, Cron } from "effect"
// Define a cron expression for 4:00 AM// on the 8th to the 14th of every monthconst expression = "0 0 4 8-14 * *"
// Parse the cron expressionconst eitherCron = Cron.parse(expression)
if (Either.isRight(eitherCron)) { // Successfully parsed console.log("Parsed cron:", eitherCron.right)} else { // Parsing failed console.error("Failed to parse cron:", eitherCron.left.message)}unsafeParse
The unsafeParse(cronExpression, tz?) function works like parse, but instead of returning an Either, it throws an exception if the input is invalid.
Example (Parsing a Cron Expression)
import { Cron } from "effect"
// Parse a cron expression for 4:00 AM// on the 8th to the 14th of every month// Throws if the expression is invalidconst cron = Cron.unsafeParse("0 0 4 8-14 * *")Checking Dates with match
The match function allows you to determine if a given Date (or any DateTime.Input) satisfies the constraints of a cron schedule.
If the date meets the schedule’s conditions, match returns true. Otherwise, it returns false.
Example (Checking if a Date Matches a Cron Schedule)
import { Cron } from "effect"
// Suppose we have a cron that triggers at 4:00 AM// on the 8th to the 14th of each monthconst cron = Cron.unsafeParse("0 0 4 8-14 * *")
const checkDate = new Date("2025-01-08 04:00:00")
console.log(Cron.match(cron, checkDate))// Output: trueFinding the Next Run
The next function determines the next date that satisfies a given cron schedule, starting from a specified date. If no starting date is provided, the current time is used as the starting point.
If next cannot find a matching date within a predefined number of iterations, it throws an error to prevent infinite loops.
Example (Determining the Next Matching Date)
import { Cron } from "effect"
// Define a cron expression for 4:00 AM// on the 8th to the 14th of every monthconst cron = Cron.unsafeParse("0 0 4 8-14 * *", "UTC")
// Specify the starting point for the searchconst after = new Date("2025-01-08")
// Find the next matching dateconst nextDate = Cron.next(cron, after)
console.log(nextDate)// Output: 2025-01-08T04:00:00.000ZIterating Over Future Dates
To generate multiple future dates that match a cron schedule, you can use the sequence function. This function provides an infinite iterator of matching dates, starting from a specified date.
Example (Generating Future Dates with an Iterator)
import { Cron } from "effect"
// Define a cron expression for 4:00 AM// on the 8th to the 14th of every monthconst cron = Cron.unsafeParse("0 0 4 8-14 * *", "UTC")
// Specify the starting dateconst start = new Date("2021-01-08")
// Create an iterator for the scheduleconst iterator = Cron.sequence(cron, start)
// Get the first matching date after the start dateconsole.log(iterator.next().value)// Output: 2021-01-08T04:00:00.000Z
// Get the second matching date after the start dateconsole.log(iterator.next().value)// Output: 2021-01-09T04:00:00.000ZConverting to Schedule
The Schedule module allows you to define recurring behaviors, such as retries or periodic events. The cron function bridges the Cron module with the Schedule module, enabling you to create schedules based on cron expressions or Cron instances.
cron
The Schedule.cron function generates a Schedule that triggers at the start of each interval defined by the provided cron expression or Cron instance. When triggered, the schedule produces a tuple [start, end] representing the timestamps (in milliseconds) of the cron interval window.
Example (Creating a Schedule from a Cron)
import { Effect, Schedule, TestClock, Fiber, TestContext, Cron, Console } from "effect"
// A helper function to log output at each interval of the scheduleconst log = <A>( action: Effect.Effect<A>, schedule: Schedule.Schedule<[number, number], void>,): void => { let i = 0
Effect.gen(function* () { const fiber: Fiber.RuntimeFiber<[[number, number], number]> = yield* Effect.gen(function* () {29 collapsed lines
yield* action i++ }).pipe( Effect.repeat( schedule.pipe( // Limit the number of iterations for the example Schedule.intersect(Schedule.recurs(10)), Schedule.tapOutput(([Out]) => Console.log(i === 11 ? "..." : [new Date(Out[0]), new Date(Out[1])]), ), ), ), Effect.fork, ) yield* TestClock.adjust(Infinity) yield* Fiber.join(fiber) }).pipe(Effect.provide(TestContext.TestContext), Effect.runPromise)}
// Build a cron that triggers at 4:00 AM// on the 8th to the 14th of each monthconst cron = Cron.unsafeParse("0 0 4 8-14 * *", "UTC")
// Convert the Cron into a Scheduleconst schedule = Schedule.cron(cron)
// Define a dummy action to repeatconst action = Effect.void
// Log the schedule intervalslog(action, schedule)/*Output:[ 1970-01-08T04:00:00.000Z, 1970-01-08T04:00:01.000Z ][ 1970-01-09T04:00:00.000Z, 1970-01-09T04:00:01.000Z ][ 1970-01-10T04:00:00.000Z, 1970-01-10T04:00:01.000Z ][ 1970-01-11T04:00:00.000Z, 1970-01-11T04:00:01.000Z ][ 1970-01-12T04:00:00.000Z, 1970-01-12T04:00:01.000Z ][ 1970-01-13T04:00:00.000Z, 1970-01-13T04:00:01.000Z ][ 1970-01-14T04:00:00.000Z, 1970-01-14T04:00:01.000Z ][ 1970-02-08T04:00:00.000Z, 1970-02-08T04:00:01.000Z ][ 1970-02-09T04:00:00.000Z, 1970-02-09T04:00:01.000Z ][ 1970-02-10T04:00:00.000Z, 1970-02-10T04:00:01.000Z ]...*/