# `Localize.DateTime.Relative`
[🔗](https://github.com/elixir-localize/localize/blob/v1.4.0/lib/localize/datetime/relative.ex#L1)

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 `t:Date.t/0`, `t:Time.t/0`, `t:NaiveDateTime.t/0` or `t: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.

# `known_units`

```elixir
@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`

```elixir
@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

* `relative` is a number, or a `t:Date.t/0`, `t:Time.t/0`, `t:NaiveDateTime.t/0` or `t:DateTime.t/0`.

* `options` is a keyword list of options.

### 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!`

```elixir
@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

* `relative` is a number, or a `t:Date.t/0`, `t:Time.t/0`, `t:NaiveDateTime.t/0` or `t:DateTime.t/0`. See `to_string/2`.

* `options` is a keyword list of options. See `to_parts/2`.

### 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`

```elixir
@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 `:unit`s, which may be fractional, or a number of seconds when there is no `:unit`. It may instead be a `t:Date.t/0`, `t:Time.t/0`, `t:NaiveDateTime.t/0` or `t: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 `t:Date.t/0` or `t:Time.t/0` is measured against a value of its own type, a `t:NaiveDateTime.t/0` or a `t:DateTime.t/0`, a `t:NaiveDateTime.t/0` against a `t:NaiveDateTime.t/0` or a `t:DateTime.t/0`, and a `t:DateTime.t/0` against a `t:DateTime.t/0`. The baseline is converted into the value's calendar, and a `t:DateTime.t/0` baseline of a `t: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!`

```elixir
@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

* `relative` is a number, or a `t:Date.t/0`, `t:Time.t/0`, `t:NaiveDateTime.t/0` or `t:DateTime.t/0`. See `to_string/2`.

* `options` is a keyword list of options.

### Options

* See `to_string/2` for the supported 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"

---

*Consult [api-reference.md](api-reference.md) for complete listing*
