Represents a Duration
A builtin type that can be referenced via :duration
Constraints
:signs- The signs the value may have, compared against zero byAsh.Type.Duration.compare/2. Any combination is permitted::positiveor[:positive]requires a positive duration,[:positive, :zero]a non-negative one, and[:positive, :negative]a non-zero one. Omit the constraint to allow any sign. This is the sign of the duration as a whole, not of each unit —%Duration{day: 1, hour: -5}is positive, being nineteen hours. Only where the year/month and week/day sides carry opposite signs does the comparison depend oncompare/2's 30-day month. Valid values are :positive, :negative, :zero:units- The units the value may be expressed in. A duration is always re-expressed in the largest of these units that will hold it, on the way in and on the way out, so[:week, :hour]turns1 week 1 day 5 hoursinto1 week 29 hours. A value that no combination of the permitted units expresses exactly is rejected — including anything that would have to cross the year/month to week/day boundary, which no conversion can. This applies on the way out as well as in: a stored duration the permitted units cannot express is refused rather than quietly rewritten. Either a single unit, an explicit list of them, or a shorthand for one side of that boundary::year_month([:year, :month]) or:day_time([:week, :day, :hour, :minute, :second, :microsecond]). Confining an attribute to a single side keeps its values comparable (seeAsh.Type.Duration.compare/2). With no constraint every unit is permitted, so the same normalization applies and nothing is ever lost.
Summary
Functions
Compares two durations as a total order, matching how the AshPostgres data
layer (PostgreSQL interval) compares them: a fixed conversion of month → 30
days and day → 24 hours (so year → 360 days, week → 7 days), down to
microseconds.
Functions
@spec compare(Duration.t(), Duration.t()) :: :lt | :eq | :gt
Compares two durations as a total order, matching how the AshPostgres data
layer (PostgreSQL interval) compares them: a fixed conversion of month → 30
days and day → 24 hours (so year → 360 days, week → 7 days), down to
microseconds.
Duration is only partially ordered in general — a month is not a fixed
number of days — which is why Elixir ships Duration without a compare/2, and
why data layers disagree on cross-unit comparison: PostgreSQL uses 30-day
months, Neo4j ~30.44-day months, and Elixir's to_timeout/1 refuses month/
year outright. This adopts PostgreSQL's convention so in-memory comparison
stays aligned with the dominant data layer rather than raising or drifting.
Within the day/time units, or within the year/month units, the result is exact
and portable across those backends; only comparison across that boundary
depends on the 30-day convention.
Computed from the integer fields directly, so microsecond precision is kept
(unlike to_timeout/1, which truncates to milliseconds).
This function is the single place the convention lives; if Elixir core later
gains a Duration.compare/2, it can delegate here.