# `Tempo.Cron`
[🔗](https://github.com/elixir-tempo/tempo/blob/v2.1.0/lib/tempo/cron.ex#L1)

Reads cron expressions into Tempo recurrences: the firings a schedule
names, as the recurrence an `RRULE` or an ISO 8601-2 repeat rule reads to.

Five fields are `minute hour day-of-month month day-of-week`; six put a
seconds field first; seven add a year last. A field is `*`, a number, a
list `1,15`, a range `1-5`, or a step `*/15`, `1-5/2`; months and days of
the week may be named (`JAN`, `MON`); Sunday is `0` or `7`. The aliases
`@yearly`, `@annually`, `@monthly`, `@weekly`, `@daily`, `@midnight` and
`@hourly` are read. Each restricted field is a part of the recurrence's
rule, the cadence is the unit above the coarsest restricted field, and
the finer fields left as `*` fire every value: `0 9 * * *` is daily at
nine, `*/15 * * * *` is four times an hour. When both the day of the
month and the day of the week are restricted, POSIX fires on either, so
the result is a set of two recurrences.

The extensions of Vixie cron and Quartz are read in the two day fields:

* `L` as the day of the month is its last day, and `LW` its last weekday.

* `15W` is the weekday nearest the 15th, in the same month: the Friday
  before a Saturday, the Monday after a Sunday, where the week is worked
  Monday to Friday. In a month that lacks the day (`31W` in June) there
  is none.

A weekday is a day of the working week of the locale the firings are
asked for in (the `:locale` option of `Tempo.to_interval/2` and
`Tempo.members/2`, or the territory in force), so `15W` under a locale
that works Sunday to Thursday keeps a Sunday and moves a Friday.

* `5L` as the day of the week is the last Friday of the month, and `5#2`
  its second Friday.

Each is one value, never a member of a list or a range.

# `parse`

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

Reads a cron expression into a recurrence.

### Arguments

* `expression` is the cron text, five to seven fields or an alias.

* `options` is a keyword list of options.

### Options

* `:from` is the first instant firings are counted from, a
  `t:Tempo.t/0` with a date. With none the rule has no start, as cron
  gives it none, and its firings are asked for in a window (`:within`).

* `:metadata` is a map carried on the recurrence and every firing.

### Returns

* `{:ok, recurrence}`, a `t:Tempo.t/0` of the `:every` form, or of the
  `:all` form holding two when both the day of the month and the day of
  the week are restricted.

* `{:error, %ArgumentError{}}` for text that is not a cron expression.

### Examples

    iex> {:ok, nine} = Tempo.Cron.parse("0 9 * * 1-5", from: ~o"2026-06-01")
    iex> Tempo.to_iso8601(nine)
    {:ok, "R/2026Y6M1D/P1M/FL{1,2,3,4,5}KT9H0MN"}

    iex> {:ok, quarter_hours} = Tempo.Cron.parse("*/15 * * * *", from: ~o"2026-06-01T00:00:00")
    iex> quarter_hours |> Tempo.to_interval!(within: ~o"2026-06-01T00") |> Tempo.IntervalSet.count()
    4

    iex> {:ok, mondays} = Tempo.Cron.parse("0 9 * * 1")
    iex> Tempo.to_iso8601(mondays)
    {:ok, "R/../P1M/FL1KT9H0MN"}

    iex> {:error, %ArgumentError{}} = Tempo.Cron.parse("@reboot")

# `parse!`

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

`parse/2`, returning the recurrence or raising.

### Arguments

* `expression` and `options` are as for `parse/2`.

### Options

* The options of `parse/2`.

### Returns

* The recurrence, a `t:Tempo.t/0`, or raises the error `parse/2` returns.

### Examples

    iex> "0 0 1 1 *" |> Tempo.Cron.parse!(from: ~o"2026-01-01") |> Tempo.to_interval!(within: ~o"2026/2027") |> Tempo.IntervalSet.count()
    2

---

*Consult [api-reference.md](api-reference.md) for complete listing*
