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
@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
setis aTempo.t/0that has spans.optionsis a keyword list of options.
Options
:withinis aTempo.t/0, the window an expression with no end of its own (a recurrence with no count) or no year is placed in.
Returns
{:ok, set}, aTempo.t/0of the:allform.{:error, exception}as forTempo.union/3.
Examples
iex> {:ok, both} = Tempo.union(~o"2026-01", ~o"2026-02")
iex> Tempo.IntervalSet.coalesce(both)
{:ok, ~o"{2026-01/2026-02}"}
coalesce/2, returning its answer or raising the error.
Arguments
- The arguments are those of
coalesce/2.
Options
- The options of
coalesce/2.
Returns
- What
coalesce/2returns 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}"
@spec count(Tempo.t()) :: non_neg_integer()
How many members a set has.
Arguments
expressionis aTempo.t/0.
Returns
- The number of its members, as
members/1gives them, raising as it raises.
Examples
iex> Tempo.IntervalSet.count(~o"{2026-06,2026-08}")
2
@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
setis aTempo.t/0that has spans.optionsis a keyword list of options.
Options
:at_leastis how many members must cover a time for it to be kept. The default is 1.:withinis aTempo.t/0, the window an expression with no end of its own (a recurrence with no count) or no year is placed in.
Returns
{:ok, set}, aTempo.t/0of the:allform.
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/2, returning its answer or raising the error.
Arguments
- The arguments are those of
covered/2.
Options
- The options of
covered/2.
Returns
- What
covered/2returns 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}"
Whether a set covers every instant of a value.
Arguments
Returns
truewhen every instant ofvalueis in a member ofset, asTempo.within?/2answers, andfalsewhen 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
@spec filter(Tempo.t(), (Tempo.t() -> as_boolean(term()))) :: Tempo.t()
The members a function keeps.
Arguments
setis aTempo.t/0.funis given each member and answers whether to keep it.
Returns
- A
Tempo.t/0of the:allform. 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"]
The first member of a set. A recurrence with no end has a first occurrence, which is its first member.
Arguments
expressionis aTempo.t/0.
Returns
- Its first member, a
Tempo.t/0, ornilfor an empty set. A recurrence counted back with no start, or a value that is no expression, raises aTempo.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"
@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
enumerableis anEnumerableofTempo.t/0, in order of start.optionsis a keyword list of options.
Options
:metadatais the set's own metadata. The default is%{}.
Returns
- A
Tempo.t/0of the:allform, whose members are read as they are asked for. A value that is no enumerable raises anArgumentError.
Examples
iex> set = Tempo.IntervalSet.from_stream(Stream.map([~o"2026-01", ~o"2026-03"], & &1))
iex> Enum.count(set)
2
The last member of a set.
Arguments
expressionis aTempo.t/0.
Returns
Examples
iex> Tempo.IntervalSet.last(~o"{2026-06,2026-08}")
~o"2026-08"
@spec map(Tempo.t(), (Tempo.t() -> term())) :: [term()] | Enumerable.t()
A function applied to each member, giving its answers.
Arguments
setis aTempo.t/0.funis given each member.
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]
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
expressionis aTempo.t/0.
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 aTempo.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"]
@spec new([Tempo.t()], keyword()) :: {:ok, Tempo.t()} | {:error, Exception.t()}
Builds a set from its members.
Arguments
membersis a list of expressions, each aTempo.t/0.optionsis a keyword list of options.
Options
:metadatais a map the caller keeps on the set. The default is%{}.:exceptis a list ofTempo.t/0the set leaves out of its members, as^writes them:{2017Y..,^2020Y}is every year from 2017 but 2020. The default is[].
Returns
{:ok, set}, aTempo.t/0of the:allform. 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 withTempo.in_zone/2.{:error, %Tempo.ConversionError{reason: :not_an_expression}}for a member that is noTempo.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]"}
Builds a set as new/2 does, returning it or raising.
Arguments
membersandoptionsare as fornew/2.
Options
- The options of
new/2.
Returns
Examples
iex> Tempo.IntervalSet.new!([~o"2026-01", ~o"2026-03"])
~o"{2026-01,2026-03}"
@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
aandbare each aTempo.t/0that has spans.
Returns
{:ok, rows}, one row per member ofa, each a list of the relations to the members ofbin order.{:error, exception}as forTempo.union/3.
Examples
iex> Tempo.IntervalSet.relation_matrix(~o"{2026-01,2026-03}", ~o"{2026-02,2026-03}")
{:ok, [[:meets, :precedes], [:met_by, :equals]]}
@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
expressionis aTempo.t/0that has spans: an interval, a set, a bounded recurrence or a value.durationis the slot length, an exactTempo.Duration.t/0.optionsis a keyword list of options.
Options
:everyis the spacing between slot starts, an exactTempo.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}, aTempo.t/0of the:allform 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
@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
- The options of
slots/3.
Returns
- What
slots/3returns 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
@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
setis aTempo.t/0.
Returns
- An
EnumerableofTempo.t/0spans. 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"]