# `Tempo.DateRule`
[🔗](https://github.com/elixir-tempo/tempo/blob/v2.1.1/lib/tempo/date_rule.ex#L1)

Reads date rules: the yearly dates people write, `2nd Monday in June`,
`Friday after 4th Thursday in November`, `easter -2`, `15 Nisan`, `chinese
01-0-01`, `march equinox in Asia/Tokyo`, into the Tempo recurrences they
mean.

The grammar is the one [date-holidays](https://github.com/commenthol/date-holidays)
writes its holidays in, read as its parser,
[date-holidays-parser](https://github.com/commenthol/date-holidays-parser),
reads it: a fixed date, a weekday step from one, Easter, an equinox or a
solstice, a date in the Islamic, Hebrew, Persian, Julian, Chinese, Korean
or Vietnamese calendar, with a span, an observed day (`and if sunday then
next monday`), year and weekday filters, `since` and `prior to`. ISO 8601-2
and RFC 5545's `RRULE` (`Tempo.RRule`) say the same things in forms made
for machines to exchange; a date rule is the form people write.

`parse/2` reads a rule, and `parse!/2` raises its error. The
[date rules guide](date-rules.md) shows how to write and use rules, with
the dates each part falls on, and [the date rule grammar](date-rule-grammar.md)
is the reference for every part a rule may have.

A part of the grammar the reading does not take returns an error naming
it: the Bengali calendar, a solar term of the Korean or Vietnamese
calendar, `if equal`, and an `if is holiday` move by more than one or
back.

# `parse`

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

Reads a date rule into the Tempo recurrence it means.

### Arguments

* `rule` is a date rule, such as `"2nd Monday in June"`.

* `options` is a keyword list of options.

### Options

* `:falls_on` is what a rule that depends on other dates is decided
  against (a bridge, `09-22 if 09-21 is holiday`, or a move, `if is holiday
  then next monday`): a map the metadata of another member of a set holds.
  The default is `%{type: type}`, the type the rule names, `:public` where
  it names none.

* `:observed` is how a rule's observed days are read: `:as_written` (the
  default); `:moved`, each clause moving the date, so that `and if sunday
  then next monday` gives the Monday in place of a Sunday rather than
  beside it; or `:none`, the date with no observed day, which leaves a rule
  that begins `substitutes`, only observed days, no date: an empty set.

* `:active` is a list of `{from, to}` windows of `t:Date.t/0`, `from`
  included and `to` not, either `nil` for an open end: the rule falls in
  the years whose date is in one of them, and within its own `since` and
  `prior to`, as date-holidays' `active` periods say.

* `:disable` is a list of `t:Date.t/0` the rule's date is moved from:
  where one is the rule's date in its year, that year's date is left out.

* `:enable` is a list of `t:Date.t/0` added in the year of a disabled
  date, the day a holiday is moved to.

* `:replaced_by` is a list of rules that begin `substitutes`, each alone
  or as `{rule, active: windows}`, that may stand in for the rule's date:
  one of the same date, gated by its years alone, takes its trigger
  weekdays from a rule that is a plain date with no observed day of its
  own, in the years it is in force. The others are passed over, so the
  `substitutes` entries of a dataset may be given to each of its rules.

### Returns

* `{:ok, expression}`, a `t:Tempo.t/0`: a yearly recurrence, a recurrence
  set of a date and its observed days, or a conditional member for a rule
  that depends on other dates. An observed day carries
  `%{substitute: true}` in its metadata.

* `{:error, %Tempo.ParseError{}}` for a rule that cannot be read, naming
  where reading stopped or which part is not read, or

* `{:error, exception}` for a date the rule names that its calendar does
  not have, such as a `Tempo.InvalidDateError` for the month of `13-01`.

### Examples

    iex> {:ok, rule} = Tempo.DateRule.parse("12-25")
    iex> Tempo.to_iso8601(rule)
    {:ok, "R/../P1Y/FL12M25DN"}

    iex> {:ok, rule} = Tempo.DateRule.parse("2nd Monday in June")
    iex> Tempo.to_iso8601(rule)
    {:ok, "R/../P1Y/FL6M1K2IN"}

    iex> {:ok, new_years_day} = Tempo.DateRule.parse("01-01 and if sunday then next monday", observed: :none)
    iex> Tempo.to_iso8601(new_years_day)
    {:ok, "R/../P1Y/FL1M1DN"}

    iex> {:error, %Tempo.ParseError{rest: "a full moon"}} = Tempo.DateRule.parse("12-25 on a full moon")

# `parse!`

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

`parse/2`, returning its answer or raising the error.

### Arguments

* The arguments are those of `parse/2`.

### Returns

* What `parse/2` returns as `{:ok, answer}`, the answer alone. The error it
  returns as `{:error, exception}` is raised.

### Examples

    iex> Tempo.DateRule.parse!("easter -2")
    ...> |> Tempo.to_iso8601!()
    "R/../P1Y/FLL(easter)eN/P-3D5K1IN"

---

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