Tempo.Cron (Tempo v0.20.0)

Copy Markdown View Source

Parses cron expressions into recurring Tempo.Interval.t/0 values — the same first-class recurrence that Tempo.RRule.parse/2 and native ISO 8601 repeating intervals produce.

Lets Tempo consume any cron-configured schedule (Oban, Quantum, system crontab) without rewriting it in another vocabulary. Once parsed, materialise it like any other recurrence — Tempo.to_interval/2 with a :bound — or supply :from to anchor the occurrences.

Supported formats

  • 5-field POSIX: "minute hour day-of-month month day-of-week".

  • 6-field (seconds-first): "second minute hour day-of-month month day-of-week". This is the variant used by Quantum and other Elixir schedulers.

  • 7-field (with year): "second minute hour day-of-month month day-of-week year". A single concrete year is converted into an UNTIL limit; a year list or range (2025,2027,2029) becomes a bymonthday-style byyear filter bounded one past the last listed year.

Field grammar

Each field accepts:

  • * — every value in the field's range.

  • N — a single integer.

  • N,M,O — a list.

  • N-M — an inclusive range.

  • */S — every S starting from the field's minimum.

  • N-M/S — every S within the range N..M.

Day-of-week accepts SUNSAT (case-insensitive) as synonyms for 06. Sunday is both 0 and 7 (cron convention); internally converted to RFC 5545's 7 (Sunday last). A day-of-week step (N/S, */S) is expanded in cron numbering — Sunday = 0, the week start — then mapped to RFC, so 0/3 is Sun, Wed, Sat and */2 is Sun, Tue, Thu, Sat. A bare-day step runs to the end of the week and does not wrap (5/2 is Fri, Sun — not Tue).

Month accepts JANDEC (case-insensitive) as synonyms for 112.

Shortcut aliases

Standard cron aliases are supported:

AliasExpands to
@yearly, @annually0 0 1 1 *
@monthly0 0 1 * *
@weekly0 0 * * 0
@daily, @midnight0 0 * * *
@hourly0 * * * *

@reboot is not supported — it is a system-startup hook, not a time expression.

Vixie-cron extensions

  • L as day-of-month — last day of the month → bymonthday: [-1].

  • NL as day-of-week (e.g. 5L) — last weekday-N of the month → byday: [{-1, N}].

  • N#K as day-of-week (e.g. 5#2) — Kth weekday-N of the month → byday: [{K, N}].

  • W as day-of-month (e.g. 15W, LW) — the nearest weekday to that day, never crossing a month boundary → bymonthday_nearest. A Saturday snaps back to Friday, a Sunday forward to Monday, and 1W on a weekend clamps forward into the same month. Only valid on a single day, never a list or range.

POSIX day-of-month OR day-of-week

When both dom and dow are restricted to plain lists (13 * 5 — "the 13th or any Friday"), POSIX cron matches the union, not the intersection. Tempo honours this via a daily cadence carrying a bymonthday_or_byday union filter, so 13 * 5 fires on every 13th and every Friday — not only Friday-the-13ths. A Quartz extension in either field (an ordinal day-of-week 5#2/5L, or a nearest-weekday 15W) opts out and keeps the AND-composing interpretation.

Not supported (AST gaps)

  • @reboot — not a time expression.

Summary

Functions

Parse a cron expression into a recurring Tempo.Interval.t/0.

Functions

parse(expression, options \\ [])

@spec parse(
  String.t(),
  keyword()
) :: {:ok, Tempo.Interval.t()} | {:error, Exception.t()}

Parse a cron expression into a recurring Tempo.Interval.t/0.

A cron schedule, an RFC 5545 RRULE, and a native ISO 8601 repeating interval all become the same kind of first-class Tempo value, so a parsed cron entry composes and materialises exactly like any other recurrence — no intermediate rule struct to manage.

Arguments

  • expression is a cron string (5, 6, or 7 fields, or an alias).

Options

  • :from — a Tempo.t/0 anchor for the recurrence. Optional; supply it to enumerate or materialise concrete occurrences.

Returns

Examples

iex> {:ok, monthly} = Tempo.Cron.parse("0 0 15 * *", from: ~o"2025-01-15")
iex> {:ok, occurrences} = Tempo.to_interval(monthly, bound: ~o"2025")
iex> Tempo.IntervalSet.count(occurrences)
12

iex> {:error, %Tempo.CronError{}} = Tempo.Cron.parse("not a cron")

parse!(expression, options \\ [])

@spec parse!(
  String.t(),
  keyword()
) :: Tempo.Interval.t()

Raising version of parse/2.

Examples

iex> Tempo.Cron.parse!("@hourly").recurrence
:infinity