Localize.DateTime.Relative (Localize v1.4.0)

Copy Markdown View Source

Formats relative times such as "3 days ago", "tomorrow" or "in 1.5 hours".

A relative time is a number of units, or the difference between a Date.t/0, Time.t/0, NaiveDateTime.t/0 or DateTime.t/0 and a baseline. The number is formatted with the locale's digits and grouping, and the plural category of the number as displayed selects the unit's pattern. to_string/2 returns the string and to_parts/2 the same result as typed parts.

A difference is counted with the arithmetic of the value's own calendar, in the calendar periods between the two: the years, quarters, months, weeks and days between their dates on the value's wall clock, and the hours, minutes and seconds between them on the value's clock at its UTC offset, so an hour that a change of offset skips or repeats is counted as the time that passes. It is never a number of seconds divided by a mean length, so a month is a month of the value's calendar whatever its length, and a Hebrew leap year has thirteen of them.

Summary

Functions

Returns the list of known time units.

Formats a relative time into typed parts, mirroring ECMA-402's formatToParts for Intl.RelativeTimeFormat.

Same as to_parts/2 but raises on error.

Returns a string representing a relative time for a given number, date, time, or datetime.

Same as to_string/2 but raises on error.

Functions

known_units()

@spec known_units() :: [atom(), ...]

Returns the list of known time units.

Returns

  • The unit atoms the :unit option accepts.

Examples

iex> Localize.DateTime.Relative.known_units()
[:day, :fri, :hour, :minute, :mon, :month, :quarter, :sat, :second, :sun, :thu, :tue, :wed, :week, :year]

to_parts(relative, options \\ [])

@spec to_parts(
  number() | Date.t() | Time.t() | NaiveDateTime.t() | DateTime.t(),
  Keyword.t()
) ::
  {:ok, [%{type: atom(), value: String.t()}]} | {:error, Exception.t()}

Formats a relative time into typed parts, mirroring ECMA-402's formatToParts for Intl.RelativeTimeFormat.

The parts concatenate to exactly the string to_string/2 produces with the same options. A named form ("yesterday") is a single :literal part. A pattern form places the number's parts from Localize.Number.to_parts/2 (:integer, :group, :decimal, :fraction), each carrying a :unit key, between :literal parts, as ECMA-402 does: "in 1,000 days" is :literal "in ", :integer "1", :group ",", :integer "000" and :literal " days".

Arguments

Options

See to_string/2 for the supported options.

Returns

  • {:ok, parts} where parts is a list of %{type: atom(), value: String.t()} maps; the number's parts also carry a :unit key.

  • {:error, exception} if relative or the options are invalid.

Examples

iex> Localize.DateTime.Relative.to_parts(-1, unit: :day, locale: :en)
{:ok, [%{type: :literal, value: "yesterday"}]}

iex> Localize.DateTime.Relative.to_parts(1000, unit: :day, locale: :en)
{:ok,
 [
   %{type: :literal, value: "in "},
   %{type: :integer, value: "1", unit: :day},
   %{type: :group, value: ",", unit: :day},
   %{type: :integer, value: "000", unit: :day},
   %{type: :literal, value: " days"}
 ]}

to_parts!(relative, options \\ [])

@spec to_parts!(
  number() | Date.t() | Time.t() | NaiveDateTime.t() | DateTime.t(),
  Keyword.t()
) :: [
  %{type: atom(), value: String.t()}
]

Same as to_parts/2 but raises on error.

Arguments

Returns

  • A list of %{type: atom(), value: String.t()} maps.

Raises

  • Raises an exception if relative or the options are invalid.

Examples

iex> Localize.DateTime.Relative.to_parts!(-1, unit: :day, locale: :en)
[%{type: :literal, value: "yesterday"}]

to_string(relative, options \\ [])

@spec to_string(
  number() | Date.t() | Time.t() | NaiveDateTime.t() | DateTime.t(),
  Keyword.t()
) ::
  {:ok, String.t()} | {:error, Exception.t()}

Returns a string representing a relative time for a given number, date, time, or datetime.

Arguments

  • relative is a number of :units, which may be fractional, or a number of seconds when there is no :unit. It may instead be a Date.t/0, Time.t/0, NaiveDateTime.t/0 or DateTime.t/0, whose difference from :relative_to is formatted.

  • options is a keyword list of options.

Options

  • :locale is a locale identifier. The number is formatted with the locale's digits and grouping, in the number system a -u-nu- extension names if it has one. The default is Localize.get_locale/0.

  • :format is :standard, :narrow, or :short. The default is :standard.

  • :unit is the time unit for formatting. One of :second, :minute, :hour, :day, :week, :month, :quarter, :year, :mon, :tue, :wed, :thu, :fri, :sat or :sun. A difference is the number of the unit's calendar periods from the baseline to the value: 1 February is "next month" from 31 January, and 00:01 is "in 1 hour" from 23:59 the day before. A week and a weekday unit count calendar weeks, each starting on the locale's first day of the week, so "next Monday" is the Monday of the week after the baseline's. Two times have no date, so no days or longer periods lie between them. If omitted, the unit is the largest of which a whole one lies between the two, from a year down to a day for dates and to a second for times. A whole day, week, month or year is reckoned as ECMA-262 Temporal reckons it: across a change of UTC offset, a day is whole once the wall clock is at or past its time on the next day and that time has passed, a skipped time being taken at the offset before the gap, and in a lunisolar calendar a year is whole on the month of the same name, a leap month's year on the ordinary month it doubles. A number with no unit is a number of seconds, which has no calendar: it is counted, to the nearest, in the largest of weeks, days, hours, minutes and seconds it reaches, and never in months, quarters or years.

  • :numeric is :auto or :always, mirroring ECMA-402's numeric option. With :auto (the default), a whole offset of -2 to 2 takes the unit's named form, such as "yesterday" or "tomorrow", where the locale has one; an offset that is no whole number, such as 1.5, is always the number. With :always, output is always numeric: "1 day ago" instead of "yesterday".

  • :relative_to is the baseline from which the difference is calculated. A Date.t/0 or Time.t/0 is measured against a value of its own type, a NaiveDateTime.t/0 or a DateTime.t/0, a NaiveDateTime.t/0 against a NaiveDateTime.t/0 or a DateTime.t/0, and a DateTime.t/0 against a DateTime.t/0. The baseline is converted into the value's calendar, and a DateTime.t/0 baseline of a DateTime.t/0 is moved into the value's time zone, so the two are compared on the value's wall clock. Any other pairing, or a baseline that cannot be converted, returns an error. The default is DateTime.utc_now/0.

Returns

  • {:ok, formatted_string} on success.

  • {:error, exception} on failure.

Examples

iex> Localize.DateTime.Relative.to_string(-1, unit: :day, locale: :en)
{:ok, "yesterday"}

iex> Localize.DateTime.Relative.to_string(-3, unit: :day, locale: :en)
{:ok, "3 days ago"}

iex> Localize.DateTime.Relative.to_string(1.5, unit: :hour, locale: :en)
{:ok, "in 1.5 hours"}

iex> Localize.DateTime.Relative.to_string(-1000, unit: :day, locale: :de)
{:ok, "vor 1.000 Tagen"}

iex> Localize.DateTime.Relative.to_string(-1, unit: :day, locale: :en, numeric: :always)
{:ok, "1 day ago"}

iex> Localize.DateTime.Relative.to_string(~D[2024-02-01], relative_to: ~D[2024-01-31], unit: :month, locale: :en)
{:ok, "next month"}

to_string!(relative, options \\ [])

@spec to_string!(
  number() | Date.t() | Time.t() | NaiveDateTime.t() | DateTime.t(),
  Keyword.t()
) ::
  String.t()

Same as to_string/2 but raises on error.

Arguments

Options

Returns

  • The localized relative time as a string.

Raises

  • Raises an exception if the relative time cannot be formatted.

Examples

iex> Localize.DateTime.Relative.to_string!(-3, unit: :day, locale: :en)
"3 days ago"

iex> Localize.DateTime.Relative.to_string!(~D[2024-06-14], relative_to: ~D[2024-06-15], locale: :en)
"yesterday"