Ecto.ParameterizedType for the PostgreSQL composite type
tempo_range — a tstzrange paired with a Tempo-resolution
string and a jsonb metadata column that together preserve
the full Tempo shape on round-trip.
CREATE TYPE tempo_range AS (
range tstzrange,
resolution text,
meta jsonb
);Use this type when you care about round-trip fidelity — the
stored value round-trips as the same %Tempo.Interval{}
including qualifications, non-Gregorian calendars,
recurrence rules, zone identifiers, and the
implicit-vs-explicit-span distinction.
Setup
Run the DDL helpers once, early in the migration history:
import Tempo.SQL.Migration
create_tempo_types()Then declare columns with add_tempo_range/2 (or the raw
add :window, :tempo_range).
Usage
schema "meetings" do
field :window, Tempo.Ecto.TempoRange
endQuery API
The standard Postgres range operators (@>, &&) do not
apply directly to a composite column — they must reach into
(column).range. Use Tempo.Ecto.QueryAPI.Composite for
fragments that auto-unwrap the composite; the plain
Tempo.Ecto.QueryAPI does not work on these columns.
Fidelity
Round-trip preserves:
Token-list resolution (
~o"2026Y"round-trips as~o"2026Y", not a materialised interval).Qualifications (
:uncertain,:approximate).Non-Gregorian calendars.
Recurrence rules and repeat rules.
Zone identifiers (IANA names preserved via the meta column, not just the UTC offset).
Tempo.Interval.metadata, provided it is JSON-serialisable.
Summary
Functions
Builds the parameterized-type tuple for use outside a schema.
Functions
Builds the parameterized-type tuple for use outside a schema.
See Tempo.Ecto.Interval.cast_type/1 for the schema-side
equivalent and the full discussion.
Arguments
optionsis a keyword list of options.
Options
:resolutiontruncates the endpoints recorded in the composite to the given component. One of:year,:month,:day,:hour,:minute, or:second. Defaults to:second.
Returns
- A
Ecto.ParameterizedType.opts/0tuple.
Examples
iex> Tempo.Ecto.TempoRange.cast_type()
{:parameterized, {Tempo.Ecto.TempoRange, %{resolution: :second}}}
iex> Tempo.Ecto.TempoRange.cast_type(resolution: :day)
{:parameterized, {Tempo.Ecto.TempoRange, %{resolution: :day}}}