Functions to create and format durations — the difference between two dates, times, or datetimes expressed in calendar units.
A duration is represented as years, months, days, hours, minutes, seconds, and microseconds. This is useful for producing human-readable strings like "11 months, 30 days" or numeric patterns like "37:48:12".
Creating durations
new/2— calculates the duration between two dates, times, or datetimes.new_from_seconds/1— creates a duration from a number of seconds.
Formatting durations
to_string/2— formats a duration as a localized string using unit names (e.g., "11 months, 30 days") viaLocalize.UnitandLocalize.List.to_time_string/2— formats the time portion of a duration using a pattern like"hh:mm:ss". Hours are unbounded (e.g., "37:48:12" for 37 hours).
Summary
Functions
Calculates the calendar duration of a Date.Range.t/0.
Calculates the calendar duration between two dates, times, or datetimes.
Same as new/2 but raises on error.
Creates a duration from a number of seconds.
Formats a duration into typed parts, mirroring ECMA-402's formatToParts for Intl.DurationFormat.
Same as to_parts/2 but raises on error.
Formats a duration as a localized string using unit names.
Same as to_string/2 but raises on error.
Formats the time portion of a duration using a numeric
pattern like "hh:mm:ss".
Same as to_time_string/2 but raises on error.
Types
@type date_or_time_or_datetime() :: Calendar.date() | Calendar.time() | Calendar.datetime() | Calendar.naive_datetime()
A date, time, naive datetime, or datetime.
@type t() :: %Localize.Duration{ day: non_neg_integer(), hour: non_neg_integer(), microsecond: {integer(), 1..6}, minute: non_neg_integer(), month: non_neg_integer(), second: non_neg_integer(), year: non_neg_integer() }
Duration in calendar units.
Functions
@spec new(Date.Range.t()) :: {:ok, t()} | {:error, Exception.t() | atom()}
Calculates the calendar duration of a Date.Range.t/0.
Equivalent to new(range.first, range.last).
Arguments
rangeis aDate.Range.t/0(e.g.,Date.range/2).
Returns
{:ok, duration}wheredurationis at/0struct.{:error, exception}if the range endpoints are incompatible.
Examples
iex> {:ok, d} = Localize.Duration.new(Date.range(~D[2019-01-01], ~D[2019-12-31]))
iex> d.month
11
@spec new(from :: date_or_time_or_datetime(), to :: date_or_time_or_datetime()) :: {:ok, t()} | {:error, Exception.t() | atom()}
Calculates the calendar duration between two dates, times, or datetimes.
The years, months and days are the span the values' own calendar adds to from to reach to, as Date.shift/2 adds it: the most years that do not pass to, then the most months after them, then the days left. They are never counted from the values' fields. A day of the month is brought into a shorter month, so 31 January to 29 February is one month. Between two datetimes the time between their times of day is added, and where to's time of day is the earlier of the two, the dates are counted to the day before to.
Two DateTime.t/0 values are two moments, measured where from is, as ECMA-262 Temporal measures two zoned date-times: to is moved to from's time zone, the years, months and days are counted on that wall clock, and the hours, minutes and seconds are the time that passes after them. So 10:00 UTC to 18:00 in Karachi is three hours, noon to noon across a change of clocks is one day, and 23:00 to 04:00 across an hour the clocks skip is four hours. Where the clocks repeat an hour the hours can be 24 or more. A time zone is known through Elixir's time zone database (Calendar.get_time_zone_database/0); without one that knows from's zone, to is taken to the UTC offset from carries.
Any other two values are measured on the wall clocks they are written in. A date paired with a datetime is taken at midnight, and a time paired with a datetime is measured against its time of day.
Arguments
fromis a date, time, or datetime representing the start.tois a date, time, or datetime representing the end. It is in the same calendar asfromand is not earlier.
Returns
{:ok, duration}wheredurationis at/0struct.{:error, exception}if the arguments are incompatible,tois earlier thanfrom, or a value is not one its calendar has. A calendar that cannot be asked for its arithmetic is aLocalize.UnknownCalendarError.t/0.
Examples
iex> {:ok, d} = Localize.Duration.new(~D[2019-01-01], ~D[2019-12-31])
iex> d.month
11
iex> {:ok, d} = Localize.Duration.new(~D[2023-01-14], ~D[2023-07-13])
iex> {d.month, d.day}
{5, 29}
iex> {:ok, d} = Localize.Duration.new(~T[10:00:00], ~T[12:30:45])
iex> {d.hour, d.minute, d.second}
{2, 30, 45}
iex> karachi = DateTime.new!(~D[2026-06-15], ~T[18:00:00], "Asia/Karachi")
iex> {:ok, d} = Localize.Duration.new(~U[2026-06-15 10:00:00Z], karachi)
iex> {d.day, d.hour}
{0, 3}
@spec new!(from :: date_or_time_or_datetime(), to :: date_or_time_or_datetime()) :: t() | no_return()
Same as new/2 but raises on error.
Arguments
fromis a date, time, or datetime representing the start.tois a date, time, or datetime representing the end.
Returns
A
t/0duration struct.Raises an exception if the arguments are incompatible.
Examples
iex> d = Localize.Duration.new!(~D[2019-01-01], ~D[2019-12-31])
iex> d.month
11
@spec new_from_seconds(seconds :: number()) :: t() | {:error, Exception.t()}
Creates a duration from a number of seconds.
The duration will contain only hours, minutes, seconds, and microseconds (year/month/day will be zero).
Arguments
secondsis a number of seconds (integer or float).
Returns
A
t/0struct.{:error, exception}ifsecondsis not a number.
Examples
iex> d = Localize.Duration.new_from_seconds(136_092)
iex> {d.hour, d.minute, d.second}
{37, 48, 12}
iex> d = Localize.Duration.new_from_seconds(90.5)
iex> {d.minute, d.second}
{1, 30}
@spec to_parts(t(), Keyword.t()) :: {:ok, [%{type: atom(), value: String.t()}]} | {:error, Exception.t()}
Formats a duration into typed parts, mirroring ECMA-402's formatToParts for Intl.DurationFormat.
The parts concatenate to exactly the string to_string/2 produces with the same options. Each duration field contributes its unit parts (from Localize.Unit.to_parts/2) with the numeric segments carrying a :unit key naming the field; the list separators between fields are :literal parts.
Arguments
durationis at/0struct.optionsis a keyword list of options.
Options
See to_string/2 for the supported options.
Returns
{:ok, parts}wherepartsis a list of%{type: atom(), value: String.t()}maps; numeric parts also carry a:unitkey.{:error, exception}if formatting fails.
Examples
iex> duration = %Localize.Duration{hour: 2, minute: 30}
iex> Localize.Duration.to_parts(duration, locale: :en)
{:ok,
[
%{type: :integer, value: "2", unit: :hour},
%{type: :literal, value: " "},
%{type: :unit, value: "hours"},
%{type: :literal, value: ", "},
%{type: :integer, value: "30", unit: :minute},
%{type: :literal, value: " "},
%{type: :unit, value: "minutes"}
]}
Same as to_parts/2 but raises on error.
Arguments
durationis at/0struct.optionsis a keyword list of options. Seeto_parts/2.
Returns
- A list of
%{type: atom(), value: String.t()}maps.
Raises
- Raises an exception if formatting fails.
Examples
iex> Localize.Duration.to_parts!(%Localize.Duration{hour: 2}, locale: :en) |> length()
3
@spec to_string(t(), Keyword.t()) :: {:ok, String.t()} | {:error, Exception.t()}
Formats a duration as a localized string using unit names.
Non-zero duration parts are formatted as units and joined
with the locale's unit list pattern for the requested width,
per ECMA-402 Intl.DurationFormat (e.g., "11 months,
30 days").
Arguments
durationis at/0struct.optionsis a keyword list of options.
Options
:exceptis a list of time unit atoms to omit from the output (e.g.,[:microsecond]). The default is[:microsecond].:localeis a locale identifier. The default isLocalize.get_locale().:formatis the display width applied to every unit: one of:long("11 months, 30 days"),:short("11 mths, 30 days"), or:narrow("11m 30d"). The default is:long. It also selects the CLDR unit list pattern that joins the parts.:displayis a keyword list of per-unit display control, mirroring ECMA-402's per-unit*Displayoptions. Each key is a unit atom (:year,:month,:day,:hour,:minute,:second,:microsecond) and each value is:auto(omit the unit when zero, the default) or:always(render the unit even when zero).:formatsis a keyword list of per-unit width overrides, mirroring ECMA-402's per-unit width options. Each key is a unit atom (the same set as:display) and each value is:long,:short, or:narrow, overriding:formatfor that unit alone; units not named keep:format. Note the plural::formatsets the width for the whole duration,:formatsoverrides individual units within it.
Returns
{:ok, formatted_string}on success.{:error, exception}if formatting fails.
Examples
iex> {:ok, d} = Localize.Duration.new(~D[2019-01-01], ~D[2019-12-31])
iex> Localize.Duration.to_string(d, locale: :en)
{:ok, "11 months, 30 days"}
iex> {:ok, d} = Localize.Duration.new(~D[2019-01-01], ~D[2019-12-31])
iex> Localize.Duration.to_string(d, locale: :en, format: :narrow)
{:ok, "11m 30d"}
iex> duration = %Localize.Duration{hour: 2}
iex> Localize.Duration.to_string(duration, locale: :en, display: [minute: :always])
{:ok, "2 hours, 0 minutes"}
iex> duration = %Localize.Duration{hour: 2, minute: 30}
iex> Localize.Duration.to_string(duration, locale: :en, formats: [hour: :narrow])
{:ok, "2h, 30 minutes"}
Same as to_string/2 but raises on error.
Arguments
durationis at/0struct.optionsis a keyword list of options.
Options
See to_string/2 for the supported options.
Returns
The formatted duration as a string.
Raises an exception if formatting fails.
Examples
iex> {:ok, d} = Localize.Duration.new(~D[2019-01-01], ~D[2019-12-31])
iex> Localize.Duration.to_string!(d, locale: :en)
"11 months, 30 days"
@spec to_time_string(t(), Keyword.t()) :: {:ok, String.t()} | {:error, Exception.t()}
Formats the time portion of a duration using a numeric
pattern like "hh:mm:ss".
Hours are unbounded — a duration of 37 hours, 48 minutes,
and 12 seconds formats as "37:48:12".
Arguments
durationis at/0struct.optionsis a keyword list of options.
Options
:formatis a format pattern string. The default is"hh:mm:ss". Use"h:mm:ss"for no zero-padding on hours, or"mm:ss"for minutes and seconds only. Each ofh,mandstakes one or two letters; a longer field formats as U+FFFD, as TR35 recommends for an invalid field.
Returns
{:ok, formatted_string}on success.{:error, exception}ifdurationis not at/0struct,optionsis not a keyword list or:formatis not a string.
Examples
iex> d = Localize.Duration.new_from_seconds(136_092)
iex> Localize.Duration.to_time_string(d)
{:ok, "37:48:12"}
iex> d = Localize.Duration.new_from_seconds(65)
iex> Localize.Duration.to_time_string(d, format: "m:ss")
{:ok, "1:05"}
Same as to_time_string/2 but raises on error.
Arguments
durationis at/0struct.optionsis a keyword list of options.
Options
See to_time_string/2 for the supported options.
Returns
The formatted time portion of the duration as a string.
Raises an exception if formatting fails.
Examples
iex> d = Localize.Duration.new_from_seconds(136_092)
iex> Localize.Duration.to_time_string!(d)
"37:48:12"
iex> d = Localize.Duration.new_from_seconds(65)
iex> Localize.Duration.to_time_string!(d, format: "m:ss")
"1:05"