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

Intervals: a span between two ends, `2026-06-01/2026-06-30`.

An interval is the `{:span, from, to}` form of a `%Tempo{}`. Each end is a
value, or one of the two is a duration (`2026-06-01/P1M`), or an end is
open (`2026-06-01/..`). Both ends are in one calendar and one zone (law 5).

A date end is the date the interval runs through, and a clock end is the
reading it runs to, as the text is read (law 9); the span the interval
denotes is half-open either way.

`new/1` builds one from its two ends, and `from/1`, `to/1` and
`endpoints/1` read them back. `instants/1` gives the instants it begins
and ends at, and `leap_seconds_spanned/1` and `spans_leap_second?/1`
the leap seconds within it.

# `endpoint`

```elixir
@type endpoint() :: Tempo.t() | Tempo.Duration.t() | :open
```

# `endpoints`

```elixir
@spec endpoints(Tempo.t()) :: {endpoint(), endpoint()}
```

Both ends of an interval.

### Arguments

* `interval` is a `t:Tempo.t/0` interval.

### Returns

* `{from, to}`, each as written. An expression that is no interval
  raises a `Tempo.ConversionError`.

### Examples

    iex> Tempo.Interval.endpoints(~o"2026-06-01/P1M")
    {~o"2026-06-01", ~o"P1M"}

# `from`

```elixir
@spec from(Tempo.t()) :: endpoint()
```

The start of an interval.

### Arguments

* `interval` is a `t:Tempo.t/0` interval.

### Returns

* The start as written: a value, a duration or `:open`. An expression
  that is no interval raises a `Tempo.ConversionError`.

### Examples

    iex> Tempo.Interval.from(~o"2026-06-01/2026-06-30")
    ~o"2026-06-01"

    iex> Tempo.Interval.from(~o"P1M/2026-06-30")
    ~o"P1M"

# `instants`

```elixir
@spec instants(Tempo.t()) ::
  {:ok,
   {NaiveDateTime.t() | DateTime.t() | nil,
    NaiveDateTime.t() | DateTime.t() | nil}}
  | {:error, Exception.t()}
```

The instants an interval runs between: the one it begins at and the one
it ends at, the end not in it. An end written to a date is read through
(law 9), so `2026-06-15/2026-06-20` ends as the 21st begins. It is what a
store of instants, a database's range of timestamps, holds of an
interval.

### Arguments

* `interval` is a `t:Tempo.t/0` interval with a year.

### Returns

* `{:ok, {from, to}}`, each a `NaiveDateTime` for an interval with no
  zone, a `DateTime` for one in a zone or at a shift, or `nil` for an
  open end, so `{nil, nil}` for `../..`. The date-times are of the
  interval's calendar.

* `{:error, exception}` for an interval with no year, as for
  `Tempo.to_interval/2`.

### Examples

    iex> Tempo.Interval.instants(~o"2026-06-15/2026-06-20")
    {:ok, {~N[2026-06-15 00:00:00], ~N[2026-06-21 00:00:00]}}

    iex> Tempo.Interval.instants(~o"2026-06-15T09:00Z/2026-06-15T17:00Z")
    {:ok, {~U[2026-06-15 09:00:00Z], ~U[2026-06-15 17:00:00Z]}}

    iex> Tempo.Interval.instants(~o"2026-06-15/..")
    {:ok, {~N[2026-06-15 00:00:00], nil}}

# `leap_seconds_spanned`

```elixir
@spec leap_seconds_spanned(Tempo.t()) ::
  [Tempo.LeapSeconds.date()] | {:error, Exception.t()}
```

The leap seconds an interval spans, each as the UTC date it ends:
`Tempo.LeapSeconds.spanned/1`, returning the list or the error.

### Arguments

* `interval` is a `t:Tempo.t/0` with a year and both its ends.

### Returns

* A list of `{year, month, day}`, empty when it spans none.

* `{:error, %Tempo.ConversionError{}}` for an interval with no year or
  with an open end.

### Examples

    iex> Tempo.Interval.leap_seconds_spanned(~o"2015-01-01/2017-12-31")
    [{2015, 6, 30}, {2016, 12, 31}]

# `new`

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

Builds an interval from its ends.

### Arguments

* `options` is a keyword list with the two ends.

### Options

* `:from` is the start: a value, a duration (when `:to` is a value), or
  `:open`.

* `:to` is the end: a value, a duration (when `:from` is a value), or
  `:open`. A date end is the date the interval runs through.

* `:metadata` is a map the caller keeps on the interval. The default is `%{}`.

### Returns

* `{:ok, interval}`, a `t:Tempo.t/0` of the `:span` form.

* `{:error, %Tempo.IntervalEndpointsError{}}` when the ends are in two
  calendars or two zones, are both durations, both open, or out of order,
  or when an end is no value, duration or `:open` (an interval or a set
  is not an end).

### Examples

    iex> Tempo.Interval.new(from: ~o"2026-06-01", to: ~o"2026-06-30")
    {:ok, ~o"2026-06-01/2026-06-30"}

    iex> Tempo.Interval.new(from: ~o"2026-06-01", to: ~o"P1M")
    {:ok, ~o"2026-06-01/P1M"}

    iex> Tempo.Interval.new(from: ~o"2026-06-01", to: :open)
    {:ok, ~o"2026-06-01/.."}

    iex> {:error, %Tempo.IntervalEndpointsError{reason: :order}} =
    ...>   Tempo.Interval.new(from: ~o"2026-06-30", to: ~o"2026-06-01")

    iex> {:error, %Tempo.IntervalEndpointsError{reason: :calendars}} =
    ...>   Tempo.Interval.new(from: ~o"2026-06-01", to: ~o"5786-03-15[u-ca=hebrew]")

# `new`

```elixir
@spec new(endpoint(), endpoint(), map()) :: {:ok, Tempo.t()} | {:error, Exception.t()}
```

Builds an interval from its two ends, as `new/1` does.

### Arguments

* `from` is the start: a value, a duration (when `to` is a value), or
  `:open`.

* `to` is the end: a value, a duration (when `from` is a value), or
  `:open`.

* `metadata` is a map the caller keeps on the interval. The default is `%{}`.

### Returns

* `{:ok, interval}` or `{:error, exception}`, as `new/1` returns.

### Examples

    iex> Tempo.Interval.new(~o"2026-06-15T09", ~o"2026-06-15T17")
    {:ok, ~o"2026-06-15T09/2026-06-15T17"}

# `new!`

```elixir
@spec new!(keyword()) :: Tempo.t()
```

Builds an interval as `new/1` does, returning it or raising.

### Arguments

* `options` is a keyword list with the two ends, as for `new/1`.

### Options

* The options of `new/1`.

### Returns

* The interval, a `t:Tempo.t/0`, or raises the error `new/1` returns.

### Examples

    iex> Tempo.Interval.new!(from: ~o"2026", to: ~o"2028")
    ~o"2026/2028"

# `spans_leap_second?`

```elixir
@spec spans_leap_second?(Tempo.t()) :: boolean()
```

Whether an interval spans a leap second, so that its length in elapsed
seconds is not its length on the clock.

### Arguments

* `interval` is a `t:Tempo.t/0` with a year and both its ends.

### Returns

* `true` or `false`; raises a `Tempo.ConversionError` for an interval
  with no year or with an open end.

### Examples

    iex> Tempo.Interval.spans_leap_second?(~o"2016-12-31T23:00:00Z/2017-01-01T01:00:00Z")
    true

    iex> Tempo.Interval.spans_leap_second?(~o"2026-06-15/2026-06-16")
    false

# `to`

```elixir
@spec to(Tempo.t()) :: endpoint()
```

The end of an interval, as written: a date end is the date the interval
runs through.

### Arguments

* `interval` is a `t:Tempo.t/0` interval.

### Returns

* The end as written: a value, a duration or `:open`. An expression that
  is no interval raises a `Tempo.ConversionError`.

### Examples

    iex> Tempo.Interval.to(~o"2026-06-01/2026-06-30")
    ~o"2026-06-30"

    iex> Tempo.Interval.to(~o"2026-06-01/..")
    :open

---

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