Builds an expression a step at a time, so that a pipeline reads as the sentence it means.
import Tempo.Builder
every(:week)
|> on(:monday)
|> at(hour: 9)
|> starting(~o"2026-06-01")
|> times(10)Every week, on Monday, at nine, starting the 1st of June, ten times.
Each step takes an expression, or the result of the step before it, and
returns {:ok, expression} or {:error, exception}. An error passes
through every step after it untouched, so a pipeline ends with the first
error it met. What each step returns is a complete expression, the one
its ISO 8601 text reads to: every(:week) |> on(:monday) is
~o"R/../P1W/FL1KN", as usable as the finished pipeline.
The steps refine any expression where they have a meaning: the same
on/3 and at/2 that refine a recurrence refine a value, so
~o"2026-06" |> on(:monday) |> at(hour: 9) is the Mondays of June 2026
at nine.
The steps, by what they say:
What repeats:
every/1.What is selected in each repeat, or in a value:
on/3for days,at/2for times of day, andlasting/2for days that run on.What a value says of itself:
uncertain/2andapproximate/2, a group within it (grouped/3), and a unit it leaves unspecified (some/2).Where a recurrence begins and ends:
starting/2,until/2,times/2andending/2.Its frame:
in_zone/2,in_calendar/2,weeks_beginning/2andin_years/3.What is left out, what is kept with it, and what goes beside it:
except/2,with_metadata/2andall_of/1.
The module is meant to be imported. Its names are English words, and
at/2 and on/3 are not Tempo.at/2 and Tempo.on/2, which place one
value on another.
Summary
Types
What each step returns.
An expression, or the result of a step: what each step takes.
A day of the week by its name.
Functions
A set of expressions, each a member: a holiday and the day it is moved to, a rule and the dates given beside it.
Marks a value approximate, as ISO 8601-2's ~ does: the whole value, or
the units named. A unit marked approximate and uncertain is both (%).
Selects a time of day: in each repeat of a recurrence, or in a value.
Where a recurrence counted back from its end ends: its last repeat ends with this value.
A recurrence of a cadence, with no start and no end yet: the first step of a rule.
An expression with some values left out of it: a set of it, and the values excluded.
The nth group of a length within a value, as ISO 8601-2 writes one
(2026Y2G3MU): the second three months of 2026.
The calendar a recurrence with no start of its own counts its years,
months and days in. The years of a domain in_years/3 gave it are read as
years of this calendar. A recurrence with a start is in its start's
calendar, and a value is converted with Tempo.to_calendar/2.
The years a recurrence repeats in: a domain of years, or every year of a kind.
An expression in a time zone: its clock readings are the zone's.
The days an expression selects, each the start of a stretch of a length: a holiday of several days.
Selects days: in each repeat of a recurrence, or in a value.
A value with one of its units left unspecified, as ISO 8601-2's X*
writes one: some day of June 2026 is 2026Y6MX*D.
Where a recurrence begins: its first repeat is the one that holds this value.
How many occurrences a recurrence has.
Marks a value uncertain, as ISO 8601-2's ? does: the whole value, or
the units named. A unit marked uncertain and approximate is both (%).
Where an expression ends, the value it runs through: a recurrence's last repeat is the one that holds it, and a value runs through it as an interval.
An expression whose weeks begin on another day than Monday: its numbered
weeks are counted from that day, and a recurrence of every second week or
more steps in those weeks. It is RFC 5545's WKST, written [u-fw=sun].
An expression carrying a caller's map: a recurrence's every occurrence carries it too.
Types
@type result() :: {:ok, Tempo.t()} | {:error, Exception.t()}
What each step returns.
@type step() :: Tempo.t() | {:ok, Tempo.t()} | {:error, Exception.t()}
An expression, or the result of a step: what each step takes.
@type weekday() ::
:monday | :tuesday | :wednesday | :thursday | :friday | :saturday | :sunday
A day of the week by its name.
Functions
A set of expressions, each a member: a holiday and the day it is moved to, a rule and the dates given beside it.
Arguments
stepsis a list of expressions, or the results of pipelines; the first error among them is returned.
Returns
{:ok, set}, a set of them, asTempo.IntervalSet.new/2makes one.{:error, exception}for an error passed on, or asTempo.IntervalSet.new/2refuses the members.
Examples
iex> import Tempo.Builder
iex> all_of([
...> every(:year) |> on(month: 12, day: 25, day_of_week: 1..5),
...> every(:year) |> on(selection: [month: 12, day: 25, day_of_week: 6..7], window: ~o"P8D", day_of_week: 1, instance: -1)
...> ])
{:ok, ~o"{R/../P1Y/FL12M25D{1..5}KN,R/../P1Y/FLL12M25D{6..7}KN/P8D1K-1IN}"}
@spec approximate(step(), :all | Tempo.unit() | [Tempo.unit()]) :: result()
Marks a value approximate, as ISO 8601-2's ~ does: the whole value, or
the units named. A unit marked approximate and uncertain is both (%).
Arguments
stepis a value or a set of them, or the result of the step before.unitsis:all, the default, or a unit or list of units the value has.
Returns
{:ok, expression}, the value marked.{:error, exception}as foruncertain/2.
Examples
iex> import Tempo.Builder
iex> approximate(~o"2026-06")
{:ok, ~o"2026-06~"}
iex> import Tempo.Builder
iex> ~o"2026-06-15" |> uncertain() |> approximate()
{:ok, ~o"2026-06-15%"}
Selects a time of day: in each repeat of a recurrence, or in a value.
Arguments
stepis an expression, or the result of the step before.timeis the time of day: its parts ([hour: 9],[hour: 9, minute: 30]) or a time of day written as a value (~o"T09:30").
Returns
{:ok, expression}, the expression with the time of day selected in it.{:error, exception}for an error passed on, a time with a date in it, or a unit the expression already selects.
Examples
iex> import Tempo.Builder
iex> every(:week) |> on(:monday) |> at(hour: 9)
{:ok, ~o"R/../P1W/FL1KT9HN"}
iex> import Tempo.Builder
iex> ~o"2026-06" |> on(:monday) |> at(~o"T09:30")
{:ok, ~o"2026Y6ML1KT9H30MN"}
Where a recurrence counted back from its end ends: its last repeat ends with this value.
Arguments
stepis a recurrence, or the result of the step before.tois aTempo.t/0value.
Returns
{:ok, recurrence}, the recurrence counted back fromto.{:error, exception}for an error passed on, or a recurrence that already has a start.
Examples
iex> import Tempo.Builder
iex> every(:month) |> ending(~o"2026-06-01") |> times(5)
{:ok, ~o"R5/P1M/2026-06-01"}
@spec every(atom() | Tempo.Duration.t() | keyword()) :: result()
A recurrence of a cadence, with no start and no end yet: the first step of a rule.
Arguments
cadenceis the length of each repeat: a unit (:year,:quarter,:month,:week,:day,:hour,:minuteor:second), aTempo.Duration.t/0, or a duration's units ([week: 2]).
Returns
{:ok, recurrence}, aTempo.t/0that repeats with no start of its own untilstarting/2gives it one.{:error, exception}for a cadence that is no length.
Examples
iex> Tempo.Builder.every(:week)
{:ok, ~o"R/../P1W"}
iex> Tempo.Builder.every(week: 2)
{:ok, ~o"R/../P2W"}
iex> Tempo.Builder.every(~o"P1M")
{:ok, ~o"R/../P1M"}
An expression with some values left out of it: a set of it, and the values excluded.
Arguments
stepis an expression, or the result of the step before.valuesis aTempo.t/0value, or a list of them.
Returns
{:ok, set}, a set of the expression's members with the values excluded, asTempo.IntervalSet.new/2makes one with:except.{:error, exception}for an error passed on, or asTempo.IntervalSet.new/2refuses it.
Examples
iex> import Tempo.Builder
iex> every(:week) |> starting(~o"2026-06-01") |> except(~o"2026-06-08")
{:ok, ~o"{R/2026-06-01/P1W,^2026-06-08}"}
@spec grouped( step(), pos_integer() | [pos_integer() | Range.t()] | Range.t(), keyword() ) :: result()
The nth group of a length within a value, as ISO 8601-2 writes one
(2026Y2G3MU): the second three months of 2026.
Arguments
stepis a value or a set of them, or the result of the step before.nthis which group: a number from 1, a list of them, or a range.lengthis the group's length, one unit and its count:[month: 3].
Returns
{:ok, expression}, the value with the group in it.{:error, exception}for an error passed on, a count that is none, a length of no one unit, or a group the value cannot hold.
Examples
iex> import Tempo.Builder
iex> grouped(~o"2026", 2, month: 3)
{:ok, ~o"2026Y2G3MU"}
iex> import Tempo.Builder
iex> {:ok, second_ten_days} = grouped(~o"2026-06", 2, day: 10)
iex> second_ten_days |> Tempo.to_interval!() |> Tempo.Interval.instants()
{:ok, {~N[2026-06-11 00:00:00], ~N[2026-06-21 00:00:00]}}
The calendar a recurrence with no start of its own counts its years,
months and days in. The years of a domain in_years/3 gave it are read as
years of this calendar. A recurrence with a start is in its start's
calendar, and a value is converted with Tempo.to_calendar/2.
Arguments
stepis a recurrence with no start, or the result of the step before.calendaris a calendar module (Calendrical.Hebrew).
Returns
{:ok, recurrence}, the recurrence in that calendar.{:error, exception}for an error passed on, a calendar Tempo does not know, or a recurrence with a start.
Examples
iex> import Tempo.Builder
iex> every(:year) |> on(month: 7, day: 1) |> in_calendar(Calendrical.Hebrew)
{:ok, ~o"R/../P1Y/FL7M1DN[u-ca=hebrew]"}
iex> import Tempo.Builder
iex> every(:year) |> on(traditional_month: 2, day: 12) |> in_years(from: 5758) |> in_calendar(Calendrical.Hebrew)
{:ok, ~o"R/{5758Y/..}/P1Y/FL2m12DN[u-ca=hebrew]"}
The years a recurrence repeats in: a domain of years, or every year of a kind.
Arguments
stepis a recurrence, or the result of the step before.yearsis either a filter, one of:even,:odd,:leapand:common, which keeps those years of every year or of the domain; or the years themselves: a year (2021), a range of them (1949..1988), the years from one on (from: 2005) or up to one (through: 2005), or a list of those. The years are of the recurrence's calendar.optionsis a keyword list of options.
Options
:exceptis a year, or a list of years, left out of the domain.
Returns
{:ok, recurrence}, the recurrence in those years only.{:error, exception}for an error passed on, years that are none of the above, a recurrence that already has a start or a domain, or a filter of a recurrence that starts at one value.
Examples
iex> import Tempo.Builder
iex> every(:year) |> on(month: 2, day: 29) |> in_years(:leap)
{:ok, ~o"R/..l/P1Y/FL2M29DN"}
iex> import Tempo.Builder
iex> every(:year) |> on(month: 9, day: 15) |> in_years(from: 2005)
{:ok, ~o"R/{2005Y/..}/P1Y/FL9M15DN"}
iex> import Tempo.Builder
iex> every(:year) |> on(month: 6, day: 19) |> in_years([from: 1961], except: 2011)
{:ok, ~o"R/{1961Y/..,^2011Y}/P1Y/FL6M19DN"}
An expression in a time zone: its clock readings are the zone's.
Arguments
stepis an expression, or the result of the step before.zoneis a time zone's name ("Europe/Paris").
Returns
{:ok, expression}, the expression in the zone, asTempo.in_zone/2places it.{:error, exception}for an error passed on, or asTempo.in_zone/2refuses it.
Examples
iex> import Tempo.Builder
iex> every(:week) |> on(:monday) |> at(hour: 9) |> in_zone("Europe/Paris")
{:ok, ~o"R/../P1W/FL1KT9HN[Europe/Paris]"}
@spec lasting(step(), Tempo.Duration.t() | keyword()) :: result()
The days an expression selects, each the start of a stretch of a length: a holiday of several days.
Arguments
stepis an expression that selects days, or the result of the step before.lengthis aTempo.Duration.t/0, or its units ([day: 8]).
Returns
{:ok, expression}, the expression selecting the stretches.{:error, exception}for an error passed on, a length that is none, or an expression that selects nothing.
Examples
iex> import Tempo.Builder
iex> every(:year) |> on(month: 8, day: 16) |> lasting(day: 2)
{:ok, ~o"R/../P1Y/FLL8M16DN/P2DN"}
Selects days: in each repeat of a recurrence, or in a value.
Arguments
stepis an expression, or the result of the step before.daysis what is selected: a day of the week by its name (:monday), a list of them, the parts of a selection of days ([day: 15],[month: 12, day: 25],[event: "easter"]), or a selectionTempo.selection/1made.optionsis a keyword list of options.
Options
:nthis which of the days a repeat holds:2for the second,-1for the last.:withinis a length counted from what the expression already selects, aTempo.Duration.t/0or its units ([day: 8],[day: -3]for the days before it): the days are selected in that window. It says the day a holiday is moved to, and a day counted from Easter.
Returns
{:ok, expression}, the expression with the days selected in it.{:error, exception}for an error passed on, days that are none of the above, or a unit the expression already selects.
Examples
iex> import Tempo.Builder
iex> every(:month) |> on(:monday, nth: 2)
{:ok, ~o"R/../P1M/FL1K2IN"}
iex> import Tempo.Builder
iex> every(:year) |> on(month: 12, day: 25)
{:ok, ~o"R/../P1Y/FL12M25DN"}
iex> import Tempo.Builder
iex> on(~o"2026-06", [:monday, :wednesday])
{:ok, ~o"2026Y6ML{1,3}KN"}Pentecost, the last Sunday in the fifty days from Easter, and a Sunday the 25th of December moved to the Monday in the eight days from it:
iex> import Tempo.Builder
iex> every(:year) |> on(event: "easter") |> on(:sunday, nth: -1, within: [day: 50])
{:ok, ~o"R/../P1Y/FLL(easter)eN/P50D7K-1IN"}
iex> import Tempo.Builder
iex> every(:year) |> on(month: 12, day: 25) |> on(:sunday) |> on(:monday, nth: -1, within: [day: 8])
{:ok, ~o"R/../P1Y/FLL12M25D7KN/P8D1K-1IN"}
@spec some(step(), Tempo.unit()) :: result()
A value with one of its units left unspecified, as ISO 8601-2's X*
writes one: some day of June 2026 is 2026Y6MX*D.
Arguments
stepis a value or a set of them, or the result of the step before.unitis the unit left unspecified, one the value does not have.
Returns
{:ok, expression}, the value with the unit unspecified.{:error, exception}for an error passed on, a unit the value already has, or one it cannot have beside its others.
Examples
iex> import Tempo.Builder
iex> some(~o"2026-06", :day)
{:ok, ~o"2026Y6MX*D"}
Where a recurrence begins: its first repeat is the one that holds this value.
Arguments
stepis a recurrence, or the result of the step before.fromis aTempo.t/0value, or a set of them, each a domain the rule is applied in (~o"{2026Y..2030Y}").
Returns
{:ok, recurrence}, the recurrence beginning there.{:error, exception}for an error passed on, or asTempo.every/2refuses the start.
Examples
iex> import Tempo.Builder
iex> every(:week) |> on(:monday) |> starting(~o"2026-06-01")
{:ok, ~o"R/2026-06-01/P1W/FL1KN"}
@spec times(step(), pos_integer()) :: result()
How many occurrences a recurrence has.
Arguments
stepis a recurrence, or the result of the step before.countis a whole number above zero.
Returns
{:ok, recurrence}, the recurrence with that many occurrences.{:error, exception}for an error passed on, a count that is not a whole number above zero, or a recurrence that already ends by a value.
Examples
iex> import Tempo.Builder
iex> every(:week) |> starting(~o"2026-06-01") |> times(10)
{:ok, ~o"R10/2026-06-01/P1W"}
@spec uncertain(step(), :all | Tempo.unit() | [Tempo.unit()]) :: result()
Marks a value uncertain, as ISO 8601-2's ? does: the whole value, or
the units named. A unit marked uncertain and approximate is both (%).
Arguments
stepis a value or a set of them, or the result of the step before. An interval's marks are on its ends: mark each value before joining them withuntil/2.unitsis:all, the default, or a unit or list of units the value has.
Returns
{:ok, expression}, the value marked.{:error, exception}for an error passed on, a unit the value does not have, or an expression that is no value.
Examples
iex> import Tempo.Builder
iex> uncertain(~o"2026-06-15")
{:ok, ~o"2026-06-15?"}
iex> import Tempo.Builder
iex> uncertain(~o"2026-06-15", :month)
{:ok, ~o"2026Y6?M15D"}
iex> import Tempo.Builder
iex> until(uncertain(~o"1984") |> elem(1), approximate(~o"2004") |> elem(1))
{:ok, ~o"1984?/2004~"}
Where an expression ends, the value it runs through: a recurrence's last repeat is the one that holds it, and a value runs through it as an interval.
Arguments
stepis a recurrence or a value, or the result of the step before.untilis aTempo.t/0value.
Returns
{:ok, expression}, the recurrence ending there, or the interval from the value throughuntil.{:error, exception}for an error passed on, a recurrence that already ends by a count, or an end before the start.
Examples
iex> import Tempo.Builder
iex> every(:day) |> starting(~o"2026-06-01") |> until(~o"2026-06-30")
{:ok, ~o"R/2026-06-01/P1D/2026-06-30"}
iex> import Tempo.Builder
iex> until(~o"2026-06-01", ~o"2026-06-12")
{:ok, ~o"2026-06-01/2026-06-12"}
An expression whose weeks begin on another day than Monday: its numbered
weeks are counted from that day, and a recurrence of every second week or
more steps in those weeks. It is RFC 5545's WKST, written [u-fw=sun].
Arguments
stepis an expression of the Gregorian calendar, or the result of the step before.dayis the weekday weeks begin on, by its name (:sunday).
Returns
{:ok, expression}, the expression with its weeks begun on that day, asTempo.to_week_start/2makes it.{:error, exception}for an error passed on, a day that is no weekday's name, or an expression of another calendar.
Examples
iex> import Tempo.Builder
iex> every(week: 2) |> on([:tuesday, :thursday]) |> weeks_beginning(:sunday)
{:ok, ~o"R/../P2W/FL{3,5}KN[u-fw=sun]"}
An expression carrying a caller's map: a recurrence's every occurrence carries it too.
Arguments
stepis an expression, or the result of the step before.metadatais a map, merged into the metadata the expression has.
Returns
{:ok, expression}, the expression with the metadata.{:error, exception}for an error passed on, or metadata that is no map.
Examples
iex> import Tempo.Builder
iex> {:ok, standup} = every(:day) |> at(hour: 9) |> with_metadata(%{title: "Standup"})
iex> Tempo.metadata(standup)
%{title: "Standup"}