Tempo.Interval (Tempo v2.1.0)

Copy Markdown View Source

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.

Summary

Functions

Both ends of an interval.

The start of an interval.

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.

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

Builds an interval from its ends.

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

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

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

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

Types

endpoint()

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

Functions

endpoints(other)

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

Both ends of an interval.

Arguments

Returns

Examples

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

from(other)

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

The start of an interval.

Arguments

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(interval)

@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 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(interval)

@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 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(options)

@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 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(from, to, metadata \\ %{})

@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!(options)

@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

Returns

Examples

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

spans_leap_second?(interval)

@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 Tempo.t/0 with a year and both its ends.

Returns

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(other)

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

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

Arguments

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