# `JSCalendar.Occurrence`
[🔗](https://github.com/elixir-tempo/jscalendar/blob/v0.1.0/lib/jscalendar/occurrence.ex#L1)

One instance of a recurring object
([RFC 8984 §4.3.5](https://www.rfc-editor.org/rfc/rfc8984.html#section-4.3.5)).

A recurring object is a base plus a rule plus a table of exceptions.
Expanding the rule gives you a list of *recurrence ids* — local
date-times — and this module turns one of those back into an object:
the base, shifted to that moment, with any override applied.

    JSCalendar.Occurrence.at(standup, ~N[2026-06-04 09:00:00])
    #=> {:ok, %JSCalendar.Event{start: ~N[2026-06-04 09:00:00], ...}}

Three things happen that a plain patch would not do:

* The **anchor moves**. An occurrence inherits everything except its
  `start` — or, for a task with no start, its `due` — which becomes
  the recurrence id. A patch may then override even that, and the
  patch wins.

* The **recurrence machinery is stripped**. §4.3.1 forbids an object
  with a `recurrenceId` from also carrying `recurrenceRules` or
  `recurrenceOverrides`, so an occurrence is a single object rather
  than a recurring one that recurs again.

* Some pointers are **ignored rather than rejected**. An override may
  not change a `uid`, a `prodId`, or the rules that generated it; the
  RFC's word is *ignored*, so such a patch still applies — minus
  those pointers — rather than failing.

Exclusion is a separate answer, not an empty one:

    JSCalendar.Occurrence.at(standup, ~N[2026-06-05 09:00:00])
    #=> :excluded

A caller expanding a rule has to distinguish "this occurrence was
cancelled" from "this occurrence could not be built", and a tuple
that could mean either would make that impossible.

# `at`

```elixir
@spec at(
  struct(),
  NaiveDateTime.t()
) :: {:ok, struct()} | :excluded | {:error, term()}
```

Build the occurrence of `object` at `recurrence_id`.

### Arguments

* `object` is a recurring `t:JSCalendar.Event.t/0` or
  `t:JSCalendar.Task.t/0`.

* `recurrence_id` is a `t:NaiveDateTime.t/0` produced by the object's
  recurrence rules, or a key of its `recurrence_overrides` that the
  rules did not produce — an additional occurrence, the equivalent of
  an iCalendar `RDATE`.

### Returns

* `{:ok, occurrence}` — the same struct type as `object`, carrying
  `recurrence_id` and `recurrence_id_time_zone`; or

* `:excluded` when the override sets `excluded` to `true`; or

* `{:error, reason}` when the override is not a valid patch.

### Examples

    iex> standup = %JSCalendar.Event{
    ...>   uid: "a", start: ~N[2026-06-01 09:00:00], time_zone: "Australia/Sydney",
    ...>   recurrence_overrides: %{~N[2026-06-03 09:00:00] => %{"title" => "Retro"}}}
    iex> {:ok, occurrence} = JSCalendar.Occurrence.at(standup, ~N[2026-06-03 09:00:00])
    iex> {occurrence.start, occurrence.title, occurrence.recurrence_id}
    {~N[2026-06-03 09:00:00], "Retro", ~N[2026-06-03 09:00:00]}

    iex> standup = %JSCalendar.Event{
    ...>   start: ~N[2026-06-01 09:00:00],
    ...>   recurrence_overrides: %{~N[2026-06-03 09:00:00] => %{"excluded" => true}}}
    iex> JSCalendar.Occurrence.at(standup, ~N[2026-06-03 09:00:00])
    :excluded

# `excluded?`

```elixir
@spec excluded?(
  struct(),
  NaiveDateTime.t()
) :: boolean()
```

Whether the occurrence at `recurrence_id` is excluded.

### Arguments

* `object` is a `t:JSCalendar.Event.t/0` or `t:JSCalendar.Task.t/0`.

* `recurrence_id` is a `t:NaiveDateTime.t/0`.

### Returns

* `true` or `false`.

### Examples

    iex> event = %JSCalendar.Event{
    ...>   recurrence_overrides: %{~N[2026-06-05 09:00:00] => %{"excluded" => true}}}
    iex> JSCalendar.Occurrence.excluded?(event, ~N[2026-06-05 09:00:00])
    true

    iex> JSCalendar.Occurrence.excluded?(%JSCalendar.Event{}, ~N[2026-06-05 09:00:00])
    false

# `overridden`

```elixir
@spec overridden(struct()) :: [NaiveDateTime.t()]
```

The recurrence ids `object` overrides, in chronological order.

Includes both variations on rule-generated occurrences and additional
ones the rules do not produce — telling those apart needs the rules
expanded, which is the caller's business, not this module's.

### Arguments

* `object` is a `t:JSCalendar.Event.t/0` or `t:JSCalendar.Task.t/0`.

### Returns

* a list of `t:NaiveDateTime.t/0`.

### Examples

    iex> event = %JSCalendar.Event{recurrence_overrides: %{
    ...>   ~N[2026-06-05 09:00:00] => %{"excluded" => true},
    ...>   ~N[2026-06-03 09:00:00] => %{"title" => "Retro"}}}
    iex> JSCalendar.Occurrence.overridden(event)
    [~N[2026-06-03 09:00:00], ~N[2026-06-05 09:00:00]]

---

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