A schedule's lifetime — how far into the future it keeps firing — read from what
a caller gives add or update into the one instant it ends.
Every schedule ends. A missing lifetime is the ceiling, 90 days, and a longer one is cut to it: nothing is refused for being long, because the node can serve the request and the answer states the end it set. The ceiling is a bound against accidents — a schedule nobody remembers, firing for ever.
The grammar
One ISO-8601 field. A span starts with P — P30D, PT12H, P2W, P1M —
and runs from the moment of the call. Anything else is an instant: with an
offset or Z it means exactly that; without one it is read in the node's time
zone, the zone the cron expression is read in. A bare date is refused, because
whether it means the start of that day or its end is a guess.
An offset-less instant the clock passes twice — in the hour a fall-back
repeats — is its first pass, as it is for a cron expression
(YmerNode.Schedules.Cron). One the clock never shows — in the hour a
spring-forward skips — is refused, with a hint to give an offset.
A lifetime that ends at or before the call is refused rather than stored as a schedule expired the moment it exists: a span of nothing, a negative span or an instant already past is a mistake best found at the keyboard.
The end is computed in UTC and truncated to the second, which is how the schedule's row holds it.
Summary
Functions
The instant a lifetime given at now ends, in UTC — nil meaning the ceiling.
Functions
The instant a lifetime given at now ends, in UTC — nil meaning the ceiling.
Examples
iex> YmerNode.Schedules.Lifetime.ends_at(nil, ~U[2026-10-01 00:00:00Z], "Etc/UTC")
{:ok, ~U[2026-12-30 00:00:00Z]}
iex> YmerNode.Schedules.Lifetime.ends_at("P30D", ~U[2026-10-01 00:00:00Z], "Etc/UTC")
{:ok, ~U[2026-10-31 00:00:00Z]}
iex> YmerNode.Schedules.Lifetime.ends_at("P1Y", ~U[2026-10-01 00:00:00Z], "Etc/UTC")
{:ok, ~U[2026-12-30 00:00:00Z]}
iex> YmerNode.Schedules.Lifetime.ends_at("2026-10-02T12:00:00+03:00", ~U[2026-10-01 00:00:00Z], "Etc/UTC")
{:ok, ~U[2026-10-02 09:00:00Z]}