Tempo.IntervalSet (Tempo v2.1.0)

Copy Markdown View Source

Interval sets: all of several expressions, {2026-06,2026-08}.

An interval set is the {:all, members} form of a %Tempo{}. Its members are in one calendar (law 5), and enumerating the set yields them (law 6). The set operations of Tempo return one.

new/2 builds one from its members and members/1 gives them back. count/1, first/1 and last/1 read it; filter/2 and map/2 work member by member, and coalesce/2 joins the members that touch. walk/1 gives the spans the set covers, covered?/2 asks whether a value lies within them, and slots/3 cuts free time into lengths. from_stream/2 makes one from a stream of expressions.

Summary

Functions

The members of a set merged into the time they cover: touching and overlapping members become one span, in order; a result is in canonical form, sorted and disjoint with no two members meeting.

coalesce/2, returning its answer or raising the error.

How many members a set has.

The time covered by at least so many members of a set at once, as spans.

covered/2, returning its answer or raising the error.

Whether a set covers every instant of a value.

The members a function keeps.

The first member of a set. A recurrence with no end has a first occurrence, which is its first member.

A set whose members are read from an enumerable as they are asked for, so a long or endless source is a set.

The last member of a set.

A function applied to each member, giving its answers.

The members of a set, or of any expression: a singular expression is its own one member, and a plural one has the members Enum gives of it (a recurrence its occurrences). One with no end, or with more members than the application's :max_values_at_once, raises a Tempo.ConversionError.

Builds a set from its members.

Builds a set as new/2 does, returning it or raising.

Allen's relation of every member of one set to every member of another.

Cuts each span an expression has into consecutive slots of a fixed length: the bookable windows in a region of free time.

slots/3, returning its answer or raising the error.

The spans of a set's members, lazily and in order: what the set covers, member by member.

Functions

coalesce(set, options \\ [])

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

The members of a set merged into the time they cover: touching and overlapping members become one span, in order; a result is in canonical form, sorted and disjoint with no two members meeting.

Arguments

  • set is a Tempo.t/0 that has spans.

  • options is a keyword list of options.

Options

  • :within is a Tempo.t/0, the window an expression with no end of its own (a recurrence with no count) or no year is placed in.

Returns

Examples

iex> {:ok, both} = Tempo.union(~o"2026-01", ~o"2026-02")
iex> Tempo.IntervalSet.coalesce(both)
{:ok, ~o"{2026-01/2026-02}"}

coalesce!(set, options \\ [])

@spec coalesce!(Tempo.t(), keyword()) :: Tempo.t()

coalesce/2, returning its answer or raising the error.

Arguments

Options

Returns

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

Examples

iex> Tempo.union!(~o"2026-01", ~o"2026-02") |> Tempo.IntervalSet.coalesce!()
~o"{2026-01/2026-02}"

count(expression)

@spec count(Tempo.t()) :: non_neg_integer()

How many members a set has.

Arguments

Returns

  • The number of its members, as members/1 gives them, raising as it raises.

Examples

iex> Tempo.IntervalSet.count(~o"{2026-06,2026-08}")
2

covered(set, options \\ [])

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

The time covered by at least so many members of a set at once, as spans.

Arguments

  • set is a Tempo.t/0 that has spans.

  • options is a keyword list of options.

Options

  • :at_least is how many members must cover a time for it to be kept. The default is 1.

  • :within is a Tempo.t/0, the window an expression with no end of its own (a recurrence with no count) or no year is placed in.

Returns

Examples

iex> {:ok, busy} = Tempo.IntervalSet.new([~o"T09/T12", ~o"T11/T14", ~o"T13/T17"])
iex> Tempo.IntervalSet.covered(busy, at_least: 2)
{:ok, ~o"{T11/T12,T13/T14}"}

covered!(set, options \\ [])

@spec covered!(Tempo.t(), keyword()) :: Tempo.t()

covered/2, returning its answer or raising the error.

Arguments

Options

Returns

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

Examples

iex> busy = Tempo.IntervalSet.new!([~o"T09/T12", ~o"T11/T14", ~o"T13/T17"])
iex> Tempo.IntervalSet.covered!(busy, at_least: 2)
~o"{T11/T12,T13/T14}"

covered?(set, value)

@spec covered?(Tempo.t(), Tempo.t()) :: boolean()

Whether a set covers every instant of a value.

Arguments

Returns

  • true when every instant of value is in a member of set, as Tempo.within?/2 answers, and false when one is not.

Examples

iex> {:ok, set} = Tempo.IntervalSet.new([~o"2026-01", ~o"2026-03"])
iex> Tempo.IntervalSet.covered?(set, ~o"2026-01-15")
true
iex> Tempo.IntervalSet.covered?(set, ~o"2026-02-15")
false

filter(set, fun)

@spec filter(Tempo.t(), (Tempo.t() -> as_boolean(term()))) :: Tempo.t()

The members a function keeps.

Arguments

  • set is a Tempo.t/0.

  • fun is given each member and answers whether to keep it.

Returns

  • A Tempo.t/0 of the :all form. A set whose members are read as they are asked for (a selection over a span with no end) is filtered as they are read, and has no last member either.

Examples

iex> {:ok, set} = Tempo.IntervalSet.new([~o"2026-01", ~o"2026-03"])
iex> Tempo.IntervalSet.filter(set, &Tempo.before?(&1, ~o"2026-02"))
~o"{2026-01}"

iex> {:ok, fifteenths} = Tempo.select(~o"2026-06-01/..", ~o"15D")
iex> fifteenths |> Tempo.IntervalSet.filter(&(Tempo.month(&1) == 12)) |> Enum.take(2)
[~o"2026-12-15", ~o"2027-12-15"]

first(expression)

@spec first(Tempo.t()) :: Tempo.t() | nil

The first member of a set. A recurrence with no end has a first occurrence, which is its first member.

Arguments

Returns

  • Its first member, a Tempo.t/0, or nil for an empty set. A recurrence counted back with no start, or a value that is no expression, raises a Tempo.ConversionError.

Examples

iex> Tempo.IntervalSet.first(~o"{2026-06,2026-08}")
~o"2026-06"

iex> Tempo.IntervalSet.first(~o"R/2026-06-01/P1W")
~o"2026-06-01/2026-06-07"

from_stream(enumerable, options \\ [])

@spec from_stream(Enumerable.t(), keyword()) :: Tempo.t()

A set whose members are read from an enumerable as they are asked for, so a long or endless source is a set.

Arguments

  • enumerable is an Enumerable of Tempo.t/0, in order of start.

  • options is a keyword list of options.

Options

  • :metadata is the set's own metadata. The default is %{}.

Returns

  • A Tempo.t/0 of the :all form, whose members are read as they are asked for. A value that is no enumerable raises an ArgumentError.

Examples

iex> set = Tempo.IntervalSet.from_stream(Stream.map([~o"2026-01", ~o"2026-03"], & &1))
iex> Enum.count(set)
2

last(expression)

@spec last(Tempo.t()) :: Tempo.t() | nil

The last member of a set.

Arguments

Returns

Examples

iex> Tempo.IntervalSet.last(~o"{2026-06,2026-08}")
~o"2026-08"

map(set, fun)

@spec map(Tempo.t(), (Tempo.t() -> term())) :: [term()] | Enumerable.t()

A function applied to each member, giving its answers.

Arguments

Returns

  • The list of the function's answers, in the members' order.

  • A stream of them for a set whose members are read as they are asked for, which may have no last member.

Examples

iex> {:ok, set} = Tempo.IntervalSet.new([~o"2026-01", ~o"2026-03"])
iex> Tempo.IntervalSet.map(set, &Tempo.to_iso8601!/1)
["2026Y1M", "2026Y3M"]

iex> {:ok, fifteenths} = Tempo.select(~o"2026-06-01/..", ~o"15D")
iex> fifteenths |> Tempo.IntervalSet.map(&Tempo.month/1) |> Enum.take(3)
[6, 7, 8]

members(set)

@spec members(Tempo.t()) :: [Tempo.t()]

The members of a set, or of any expression: a singular expression is its own one member, and a plural one has the members Enum gives of it (a recurrence its occurrences). One with no end, or with more members than the application's :max_values_at_once, raises a Tempo.ConversionError.

Arguments

Returns

  • The list of its members, each a Tempo.t/0. An expression with no end, one of too many members, or a value that is no expression raises a Tempo.ConversionError.

Examples

iex> Tempo.IntervalSet.members(~o"{2026-06,2026-08}")
[~o"2026-06", ~o"2026-08"]

iex> Tempo.IntervalSet.members(~o"2026-06")
[~o"2026-06"]

iex> Tempo.IntervalSet.members(~o"R2/2026-06-01/P1W")
[~o"2026-06-01/2026-06-07", ~o"2026-06-08/2026-06-14"]

new(members, options \\ [])

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

Builds a set from its members.

Arguments

  • members is a list of expressions, each a Tempo.t/0.

  • options is a keyword list of options.

Options

  • :metadata is a map the caller keeps on the set. The default is %{}.

  • :except is a list of Tempo.t/0 the set leaves out of its members, as ^ writes them: {2017Y..,^2020Y} is every year from 2017 but 2020. The default is [].

Returns

  • {:ok, set}, a Tempo.t/0 of the :all form. A set is one expression, in one calendar and one zone: a member of another calendar than the first's is converted to it, and where a member is in a named zone every zoned member is moved to the first such zone.

  • {:error, %Tempo.FrameError{}} when one member is zoned and another has no zone: place it with Tempo.in_zone/2.

  • {:error, %Tempo.ConversionError{reason: :not_an_expression}} for a member that is no Tempo.t/0, and {:error, %ArgumentError{}} for members that are no list.

Examples

iex> Tempo.IntervalSet.new([~o"2026-06", ~o"2026-08"])
{:ok, ~o"{2026-06,2026-08}"}

iex> {:ok, from_2017} = Tempo.Interval.new(from: Tempo.new!(year: 2017), to: :open)
iex> Tempo.IntervalSet.new([from_2017], except: [Tempo.new!(year: 2020)])
{:ok, ~o"{2017Y..,^2020Y}"}

iex> {:ok, set} = Tempo.IntervalSet.new([~o"2026-06-01/2026-06-15", ~o"2026-07"])
iex> Tempo.IntervalSet.count(set)
2

iex> Tempo.IntervalSet.new([~o"2026-06-01T09/2026-06-01T10[Europe/London]", ~o"2026-06-01T09/2026-06-01T10[America/New_York]"])
{:ok, ~o"{2026-06-01T09/2026-06-01T10,2026-06-01T14/2026-06-01T15}[Europe/London]"}

new!(members, options \\ [])

@spec new!([Tempo.t()], keyword()) :: Tempo.t()

Builds a set as new/2 does, returning it or raising.

Arguments

  • members and options are as for new/2.

Options

Returns

Examples

iex> Tempo.IntervalSet.new!([~o"2026-01", ~o"2026-03"])
~o"{2026-01,2026-03}"

relation_matrix(a, b)

@spec relation_matrix(Tempo.t(), Tempo.t()) ::
  {:ok, [[Tempo.Allen.relation()]]} | {:error, Exception.t()}

Allen's relation of every member of one set to every member of another.

Arguments

Returns

  • {:ok, rows}, one row per member of a, each a list of the relations to the members of b in order.

  • {:error, exception} as for Tempo.union/3.

Examples

iex> Tempo.IntervalSet.relation_matrix(~o"{2026-01,2026-03}", ~o"{2026-02,2026-03}")
{:ok, [[:meets, :precedes], [:met_by, :equals]]}

slots(expression, duration, options \\ [])

@spec slots(Tempo.t(), Tempo.Duration.t(), keyword()) ::
  {:ok, Tempo.t()} | {:error, Exception.t()}

Cuts each span an expression has into consecutive slots of a fixed length: the bookable windows in a region of free time.

Within each span, slots of the duration are laid from its start, each starting one :every after the last, and kept when they fit inside the span. Each slot carries its span's metadata. The spans stay distinct: the result is not coalesced.

Arguments

  • expression is a Tempo.t/0 that has spans: an interval, a set, a bounded recurrence or a value.

  • duration is the slot length, an exact Tempo.Duration.t/0.

  • options is a keyword list of options.

Options

  • :every is the spacing between slot starts, an exact Tempo.Duration.t/0. The default is the slot length, so slots are adjacent; a smaller spacing overlaps them, a larger leaves gaps.

Returns

  • {:ok, set}, a Tempo.t/0 of the :all form holding the slots in order.

  • {:error, %ArgumentError{}} when a duration has a month or a year, which has no fixed length, {:error, %Tempo.ConversionError{reason: :unbounded}} for an expression with no end, whose slots cannot all be listed, and {:error, exception} when the expression has no spans.

Examples

iex> work = ~o"2026-06-15T09:00/2026-06-15T12:00"
iex> {:ok, slots} = Tempo.IntervalSet.slots(work, ~o"PT1H")
iex> Tempo.IntervalSet.count(slots)
3

iex> work = ~o"2026-06-15T09:00/2026-06-15T12:00"
iex> {:ok, slots} = Tempo.IntervalSet.slots(work, ~o"PT1H", every: ~o"PT30M")
iex> Tempo.IntervalSet.count(slots)
5

slots!(expression, duration, options \\ [])

@spec slots!(Tempo.t(), Tempo.Duration.t(), keyword()) :: Tempo.t()

slots/3, returning its answer or raising the error.

Arguments

  • The arguments are those of slots/3.

Options

Returns

  • What slots/3 returns as {:ok, answer}, the answer alone. The error it returns as {:error, exception} is raised.

Examples

iex> ~o"2026-06-15T09:00/2026-06-15T12:00" |> Tempo.IntervalSet.slots!(~o"PT1H") |> Tempo.IntervalSet.count()
3

walk(set)

@spec walk(Tempo.t()) :: Enumerable.t()

The spans of a set's members, lazily and in order: what the set covers, member by member.

Arguments

Returns

  • An Enumerable of Tempo.t/0 spans. An expression with no spans, or that is no expression, raises its error.

Examples

iex> {:ok, set} = Tempo.IntervalSet.new([~o"2026-01", ~o"2026-03"])
iex> set |> Tempo.IntervalSet.walk() |> Enum.map(&Tempo.to_iso8601!/1)
["2026Y1M/2026Y1M", "2026Y3M/2026Y3M"]