Formats date and time intervals as localized strings.
Interval formats produce strings like "Jan 10 – 12, 2008" from two dates, rather than repeating "Jan 10, 2008 – Jan 12, 2008". The format is selected based on the greatest calendar field difference between the start and end values.
Summary
Functions
Returns the greatest calendar field difference between two dates or datetimes.
Returns the locale-independent skeletons for the :fields option of to_string/3.
Splits an interval format string into [left, right] halves
at the point where a format character repeats.
Formats a date, time, or datetime interval into typed parts, mirroring ECMA-402's formatRangeToParts.
Same as to_parts/3 but raises on error.
Formats a date interval as a localized string.
Same as to_string/3 but raises on error.
Functions
@spec greatest_difference(map(), map()) :: {:ok, :y | :M | :d | :H | :m} | {:error, Exception.t()}
Returns the greatest calendar field difference between two dates or datetimes.
Arguments
fromis a date or datetime map.tois a date or datetime map.
Returns
{:ok, field}where field is:y,:M,:d,:H, or:m.{:error, %Localize.NoPracticalDifferenceError{}}if the values are equal at every field considered.
Examples
iex> Localize.Interval.greatest_difference(~D[2022-04-22], ~D[2022-04-27])
{:ok, :d}
iex> Localize.Interval.greatest_difference(~D[2021-12-31], ~D[2022-01-01])
{:ok, :y}
@spec known_fields() :: %{
month: %{short: :M, medium: :MMM, long: :MMM, full: :MMM},
month_and_day: %{short: :Md, medium: :MMMd, long: :MMMEd, full: :MMMEd},
year_and_month: %{short: :yM, medium: :yMMM, long: :yMMMM, full: :yMMMM}
}
Returns the locale-independent skeletons for the :fields option of to_string/3.
Only the non-default :fields selections (:month,
:month_and_day, :year_and_month) appear here, because only
those are locale-independent. The default :date selection is
resolved per-locale, mirroring Localize.Date.to_string/2's
:format → skeleton mapping for that locale, so it has no fixed
entry to list.
Returns
- A map keyed by field selection (
:month,:month_and_day,:year_and_month), each value a map of:format(:short,:medium,:long,:full) to the CLDR skeleton atom used for that combination.
Examples
iex> Localize.Interval.known_fields()
%{
month: %{short: :M, full: :MMM, long: :MMM, medium: :MMM},
month_and_day: %{short: :Md, full: :MMMEd, long: :MMMEd, medium: :MMMd},
year_and_month: %{short: :yM, full: :yMMMM, long: :yMMMM, medium: :yMMM}
}
@spec split_interval(String.t()) :: {:ok, [String.t()]} | {:error, Exception.t()}
Splits an interval format string into [left, right] halves
at the point where a format character repeats.
Arguments
intervalis an interval format pattern string.
Returns
{:ok, [left, right]}whereleftandrightare the two halves of the interval pattern.{:error, exception}if the pattern is malformed or has no repeating field.
Examples
iex> Localize.Interval.split_interval("MMM d – d")
{:ok, ["MMM d – ", "d"]}
@spec to_parts(map(), map(), Keyword.t()) :: {:ok, [%{type: atom(), value: String.t(), source: atom()}]} | {:error, Exception.t()}
Formats a date, time, or datetime interval into typed parts, mirroring ECMA-402's formatRangeToParts.
The parts concatenate to exactly the string to_string/3 produces with the same options. Every part carries a :source key: :start_range for parts of the interval start, :end_range for parts of the interval end, and :shared for the separators between them. When the endpoints have no practical difference the single formatted value carries source :shared throughout.
Unlike to_string/3, open intervals (a nil endpoint) are not supported — both endpoints are required, matching the JS API.
Arguments
fromis aDate,Time,DateTime,NaiveDateTime, or compatible map for the interval start.tois a value of the same kind for the interval end.optionsis a keyword list of options. Seeto_string/3.
Returns
{:ok, parts}wherepartsis a list of%{type: atom(), value: String.t(), source: atom()}maps.{:error, exception}on failure or when an endpoint isnil.
Examples
iex> Localize.Interval.to_parts(~D[2022-04-22], ~D[2022-04-25], locale: :en)
{:ok,
[
%{type: :month, value: "Apr", source: :start_range},
%{type: :literal, value: " ", source: :start_range},
%{type: :day, value: "22", source: :start_range},
%{type: :literal, value: " – ", source: :shared},
%{type: :day, value: "25", source: :end_range},
%{type: :literal, value: ", ", source: :end_range},
%{type: :year, value: "2022", source: :end_range}
]}
@spec to_parts!(map(), map(), Keyword.t()) :: [ %{type: atom(), value: String.t(), source: atom()} ]
Same as to_parts/3 but raises on error.
Arguments
fromis the interval start.tois the interval end.optionsis a keyword list of options. Seeto_parts/3.
Returns
- A list of
%{type: atom(), value: String.t(), source: atom()}maps.
Raises
- Raises an exception if the interval cannot be decomposed into parts.
Examples
iex> Localize.Interval.to_parts!(~D[2022-04-22], ~D[2022-04-25], locale: :en) |> length()
7
@spec to_string(map() | nil, map() | nil, Keyword.t()) :: {:ok, String.t()} | {:error, Exception.t()}
Formats a date interval as a localized string.
Arguments
Options
:localeis a locale identifier. The default is:en.:fieldsselects which date fields appear::date(the whole date, the default),:month,:month_and_day, or:year_and_month. Seeknown_fields/0.:formatselects how wide those fields are rendered::short,:medium,:long, or:full. The default is:medium.
The two are independent axes: :fields chooses which fields
appear, :format chooses how wide they are rendered. So
fields: :year_and_month renders the two months against a
single year either way — numerically for format: :short
("1/2022" … "3/2022") and spelled out for format: :long
("January" … "March 2022").
Returns
{:ok, formatted_string}on success.{:error, exception}on failure.
Examples
iex> {:ok, result} = Localize.Interval.to_string(~D[2022-04-22], ~D[2022-04-25], locale: :en)
iex> String.contains?(result, "Apr")
true
iex> {:ok, result} = Localize.Interval.to_string(~D[2022-01-15], ~D[2022-03-20], locale: :en)
iex> String.contains?(result, "Jan") and String.contains?(result, "Mar")
true
Same as to_string/3 but raises on error.
Arguments
Options
See to_string/3 for the supported options.
Returns
The formatted interval as a string.
Raises an exception if the interval cannot be formatted.
Examples
iex> Localize.Interval.to_string!(~D[2022-04-22], ~D[2022-04-25], locale: :en)
"Apr 22 – 25, 2022"
iex> Localize.Interval.to_string!(~D[2022-01-15], ~D[2022-03-20], locale: :en)
"Jan 15 – Mar 20, 2022"