README.md

# Date and Time Localization and Formatting

![Build status](https://github.com/elixir-cldr/cldr_dates_times/actions/workflows/ci.yml/badge.svg)
[![Hex.pm](https://img.shields.io/hexpm/v/ex_cldr_dates_times.svg)](https://hex.pm/packages/ex_cldr_dates_times)
[![Hex.pm](https://img.shields.io/hexpm/dw/ex_cldr_dates_times.svg?)](https://hex.pm/packages/ex_cldr_dates_times)
[![Hex.pm](https://img.shields.io/hexpm/dt/ex_cldr_dates_times.svg?)](https://hex.pm/packages/ex_cldr_dates_times)
[![Hex.pm](https://img.shields.io/hexpm/l/ex_cldr_dates_times.svg)](https://hex.pm/packages/ex_cldr_dates_times)

## Installation

**Note that `ex_cldr_dates_times` requires Elixir 1.12 or later.**

Add `ex_cldr_dates_time` as a dependency to your `mix` project:

```
defp deps do
  [
    {:ex_cldr_dates_times, "~> 2.0"}
  ]
end
```

then retrieve `ex_cldr_dates_times` from [hex](https://hex.pm/packages/ex_cldr_dates_times):

```
mix deps.get
mix deps.compile
```

### Configuring a required backend module

`ex_cldr_dates_times` uses the configuration set for the dependency `ex_cldr`.  See the documentation for [ex_cldr](https://hexdocs.pm/ex_cldr/2.0.0/readme.html#configuration).

A `backend` module is required that is used to host the functions that manage CLDR data.  An example to get started is:

1. Create a backend module (see [ex_cldr](https://hexdocs.pm/ex_cldr/2.0.0/readme.html#configuration) for details of the available options). Note the requirement to configure the appropriate `Cldr` provider backends.

    ```elixir
    defmodule MyApp.Cldr do
      use Cldr,
        locales: ["en", "fr", "ja"],
        providers: [Cldr.Number, Cldr.Calendar, Cldr.DateTime]

    end
    ```

2. [Optional] Update `config.exs` configuration to specify this backend as the system default. Not required, but often useful.

    ```elixir
    config :ex_cldr,
      default_locale: "en",
      default_backend: MyApp.Cldr
    ```

## Introduction

`ex_cldr_dates_times` is an addon library application for [ex_cldr](https://hex.pm/packages/ex_cldr) that provides localisation and formatting for dates, times and date_times.

The primary API is `MyApp.Cldr.Date.to_string/2`, `MyApp.Cldr.Time.to_string/2`, `MyApp.Cldr.DateTime.to_string/2` and `MyApp.Cldr.DateTime.Relative.to_string/2`.  In the following examples `MyApp` refers to a CLDR backend module that must be defined by the developer:

```elixir
iex> MyApp.Cldr.Date.to_string ~D[2020-05-30]
{:ok, "May 30, 2020"}

iex> MyApp.Cldr.Time.to_string ~U[2020-05-30 03:52:56Z]
{:ok, "3:52:56 AM"}

iex> MyApp.Cldr.DateTime.to_string ~U[2020-05-30 03:52:56Z]
{:ok, "May 30, 2020, 3:52:56 AM"}

# Note that if options are provided, a backend
# module is also required
iex> MyApp.Cldr.DateTime.Relative.to_string 1, unit: :day, format: :narrow
{:ok, "tomorrow"}
```

For help in `iex`:

```elixir
iex> h MyApp.Cldr.Date.to_string
iex> h MyApp.Cldr.Time.to_string
iex> h MyApp.Cldr.DateTime.to_string
iex> h MyApp.Cldr.DateTime.Relative.to_string
```

## Date, Time and DateTime Localization Formats

Dates, Times and DateTimes can be formatted using standard formats, format strings or format IDs.

* Standard formats provide cross-locale standardisation and therefore should be preferred where possible.  The format types, implemented for `MyApp.Cldr.Date.to_string/2`, `MyApp.Cldr.Time.to_string/2`,`MyApp.Cldr.DateTime.to_string/2` are `:short`, `:medium`, `:long`  and `:full`.   The default is `:medium`. For example, assuming a configured backend called `MyApp.Cldr`:

    ```elixir
    iex> MyApp.Cldr.DateTime.to_string ~U[2020-05-30 03:52:56Z], format: :short
    {:ok, "5/30/20, 3:52 AM"}

    iex> MyApp.Cldr.DateTime.to_string ~U[2020-05-30 03:52:56Z], format: :long
    {:ok, "May 30, 2020 at 3:52:56 AM UTC"}

    iex> MyApp.Cldr.DateTime.to_string ~U[2020-05-30 03:52:56Z], format: :medium
    {:ok, "May 30, 2020, 3:52:56 AM"}

    iex> MyApp.Cldr.DateTime.to_string ~U[2020-05-30 03:52:56Z], format: :long, locale: "fr"
    {:ok, "30 mai 2020 à 03:52:56 UTC"}
    ```

* Format strings use one or more formatting symbols to define what date and time elements should be placed in the format.  A simple example to format the time into hours and minutes:

    ```elixir
    iex> MyApp.Cldr.DateTime.to_string ~U[2020-05-30 03:52:56Z], format: "hh:mm"
    {:ok, "03:52"}
    ```

* Format IDs are an atom that is a key into a map of formats defined by CLDR. These format IDs are returned by `MyApp.Cldr.DateTime.Format.date_time_available_formats/2` (assuming your backend is `MyApp.Cldr`).  The set of common format names across all locales configured in `ex_cldr` can be returned by `MyApp.Cldr.DateTime.Format.common_date_time_format_names/0`.

    ```elixir
    iex> MyApp.Cldr.DateTime.Format.date_time_available_formats()
    %{mmmm_w_count_one: "'week' W 'of' MMMM", gy_mmm: "MMM y G", md: "M/d",
    mmm_md: "MMMM d", e_hms: "E HH:mm:ss", ed: "d E", y_mmm: "MMM y",
    e_hm: "E HH:mm", mmm_ed: "E, MMM d", y_mmm_ed: "E, MMM d, y",
    gy_mm_md: "MMM d, y G", mmm: "LLL", y_md: "M/d/y", gy: "y G",
    hms: "h:mm:ss a", hm: "h:mm a", y_mmmm: "MMMM y", m: "L",
    gy_mmm_ed: "E, MMM d, y G", y_qqq: "QQQ y", e: "ccc", y_qqqq: "QQQQ y",
    hmsv: "h:mm:ss a v", mmmm_w_count_other: "'week' W 'of' MMMM",
    ehm: "E h:mm a", y_m_ed: "E, M/d/y", h: "h a", hmv: "h:mm a v",
    yw_count_other: "'week' w 'of' y", mm_md: "MMM d", y_m: "M/y", m_ed: "E, M/d",
    ms: "mm:ss", d: "d", y_mm_md: "MMM d, y", yw_count_one: "'week' w 'of' y",
    y: "y", ehms: "E h:mm:ss a"}

    # These format types can be invoked for any locale - meaning
    # these format names are defined for all configured locales.
    iex> MyApp.Cldr.DateTime.Format.common_date_time_format_names()
    [:gy_mmm, :md, :mmm_md, :e_hms, :ed, :y_mmm, :e_hm, :mmm_ed, :y_mmm_ed,
    :gy_mm_md, :mmm, :y_md, :gy, :hms, :hm, :y_mmmm, :m, :gy_mmm_ed, :y_qqq, :e,
    :y_qqqq, :hmsv, :mmmm_w_count_other, :ehm, :y_m_ed, :h, :hmv, :yw_count_other,
    :mm_md, :y_m, :m_ed, :ms, :d, :y_mm_md, :y, :ehms]

    iex> Cldr.DateTime.to_string ~U[2020-05-30 03:52:56Z], MyApp.Cldr, format: :gy_mmm_ed
    {:ok, "Sat, May 30, 2020 AD"}

    iex(2)> Cldr.Time.to_string ~U[2020-05-30 03:52:56Z], MyApp.Cldr, format: :hm
    {:ok, "3:52 AM"}

    iex(3)> Cldr.Date.to_string ~U[2020-05-30 03:52:56Z], MyApp.Cldr, format: :yMd
    {:ok, "5/30/2020"}
    ```

## Format strings

The [CLDR standard](http://unicode.org/reports/tr35/tr35-dates.html#Date_Field_Symbol_Table)
defines a wide range of format symbols.  Most - but not all - of these symbols are supported in
`Cldr`.  The supported symbols are described below.  Note the [known restrictions and limitations](#known-restrictions-and-limitations).

| Element                | Symbol     | Example         | Cldr Format                        |
| :--------------------  | :--------  | :-------------- | :--------------------------------- |
| Era                    | G, GG, GGG | "AD"            | Abbreviated                        |
|                        | GGGG       | "Anno Domini"   | Wide                               |
|                        | GGGGG      | "A"             | Narrow                             |
| Year                   | y          | 7               | Minimum necessary digits           |
|                        | yy         | "17"            | Least significant 2 digits         |
|                        | yyy        | "017", "2017"   | Padded to at least 3 digits        |
|                        | yyyy       | "2017"          | Padded to at least 4 digits        |
|                        | yyyyy      | "02017"         | Padded to at least 5 digits        |
| ISOWeek Year           | Y          | 7               | Minimum necessary digits           |
|                        | YY         | "17"            | Least significant 2 digits         |
|                        | YYY        | "017", "2017"   | Padded to at least 3 digits        |
|                        | YYYY       | "2017"          | Padded to at least 4 digits        |
|                        | YYYYY      | "02017"         | Padded to at least 5 digits        |
| Related Gregorian Year | r, rr, rr+ | 2017            | Minimum necessary digits           |
| Cyclic Year            | U, UU, UUU | "甲子"           | Abbreviated                        |
|                        | UUUU       | "甲子" (for now) | Wide                               |
|                        | UUUUU      | "甲子" (for now) | Narrow                             |
| Extended Year          | u+         | 4601            | Minimum necessary digits           |
| Quarter                | Q          | 2               | Single digit                       |
|                        | QQ         | "02"            | Two digits                         |
|                        | QQQ        | "Q2"            | Abbreviated                        |
|                        | QQQQ       | "2nd quarter"   | Wide                               |
|                        | QQQQQ      | "2"             | Narrow                             |
| Standalone Quarter     | q          | 2               | Single digit                       |
|                        | qq         | "02"            | Two digits                         |
|                        | qqq        | "Q2"            | Abbreviated                        |
|                        | qqqq       | "2nd quarter"   | Wide                               |
|                        | qqqqq      | "2"             | Narrow                             |
| Month                  | M          | 9               | Single digit                       |
|                        | MM         | "09"            | Two digits                         |
|                        | MMM        | "Sep"           | Abbreviated                        |
|                        | MMMM       | "September"     | Wide                               |
|                        | MMMMM      | "S"             | Narrow                             |
| Standalone Month       | L          | 9               | Single digit                       |
|                        | LL         | "09"            | Two digits                         |
|                        | LLL        | "Sep"           | Abbreviated                        |
|                        | LLLL       | "September"     | Wide                               |
|                        | LLLLL      | "S"             | Narrow                             |
| Week of Year           | w          | 2, 22           | Single digit                       |
|                        | ww         | 02, 22          | Two digits, zero padded            |
| Week of Month          | W          | 2               | Single digit. NOT IMPLEMENTED YET  |
| Day of Year            | D          | 3, 33, 333      | Minimum necessary digits           |
|                        | DD         | 03, 33, 333     | Minimum of 2 digits, zero padded   |
|                        | DDD        | 003, 033, 333   | Minimum of 3 digits, zero padded   |
| Day of Month           | d          | 2, 22           | Minimum necessary digits           |
|                        | dd         | 02, 22          | Two digits, zero padded            |
| Day of Week            | E, EE, EEE | "Tue"           | Abbreviated                        |
|                        | EEEE       | "Tuesday"       | Wide                               |
|                        | EEEEE      | "T"             | Narrow                             |
|                        | EEEEEE     | "Tu"            | Short                              |
|                        | e          | 2               | Single digit                       |
|                        | ee         | "02"            | Two digits                         |
|                        | eee        | "Tue"           | Abbreviated                        |
|                        | eeee       | "Tuesday"       | Wide                               |
|                        | eeeee      | "T"             | Narrow                             |
|                        | eeeeee     | "Tu"            | Short                              |
| Standalone Day of Week | c, cc      | 2               | Single digit                       |
|                        | ccc        | "Tue"           | Abbreviated                        |
|                        | cccc       | "Tuesday"       | Wide                               |
|                        | ccccc      | "T"             | Narrow                             |
|                        | cccccc     | "Tu"            | Short                              |
| AM or PM               | a, aa, aaa | "am."           | Abbreviated                        |
|                        | aaaa       | "am."           | Wide                               |
|                        | aaaaa      | "am"            | Narrow                             |
| Noon, Mid, AM, PM      | b, bb, bbb | "mid."          | Abbreviated                        |
|                        | bbbb       | "midnight"      | Wide                               |
|                        | bbbbb      | "md"            | Narrow                             |
| Flexible time period   | B, BB, BBB | "at night"      | Abbreviated                        |
|                        | BBBB       | "at night"      | Wide                               |
|                        | BBBBB      | "at night"      | Narrow                             |
| Hour                   | h, K, H, k |                 | See the table below                |
| Minute                 | m          | 3, 10           | Minimum digits of minutes          |
|                        | mm         | "03", "12"      | Two digits, zero padded            |
| Second                 | s          | 3, 48           | Minimum digits of seconds          |
|                        | ss         | "03", "48"      | Two digits, zero padded            |
| Fractional Seconds     | S          | 3, 48           | Minimum digits of fractional seconds |
|                        | SS         | "03", "48"      | Two digits, zero padded            |
| Milliseconds            | A+         | 4000, 63241     | Minimum digits of milliseconds since midnight |
| Generic non-location TZ | v         | "Etc/UTC"       | `:time_zone` key, unlocalised      |
|                         | vvvv      | "unk"           | Generic timezone name.  Currently returns only "unk" |
| Specific non-location TZ | z..zzz   | "UTC"           | `:zone_abbr` key, unlocalised      |
|                         | zzzz      | "GMT"           | Delegates to `zone_gmt/4`          |
| Timezone ID             | V         | "unk"           | `:zone_abbr` key, unlocalised      |
|                         | VV        | "Etc/UTC        | Delegates to `zone_gmt/4`          |
|                         | VVV       | "Unknown City"  | Exemplar city.  Not supported.     |
|                         | VVVV      | "GMT"           | Delegates to `zone_gmt/4           |
| ISO8601 Format          | Z..ZZZ    | "+0100"         | ISO8601 Basic Format with hours and minutes |
|                         | ZZZZ      | "+01:00"        | Delegates to `zone_gmt/4           |
|                         | ZZZZZ     | "+01:00:10"     | ISO8601 Extended format with optional seconds |
| ISO8601 plus Z          | X         | "+01"           | ISO8601 Basic Format with hours and optional minutes or "Z" |
|                         | XX        | "+0100"         | ISO8601 Basic Format with hours and minutes or "Z"          |
|                         | XXX       | "+0100"         | ISO8601 Basic Format with hours and minutes, optional seconds or "Z" |
|                         | XXXX      | "+010059"       | ISO8601 Basic Format with hours and minutes, optional seconds or "Z" |
|                         | XXXXX     | "+01:00:10"     | ISO8601 Extended Format with hours and minutes, optional seconds or "Z" |
| ISO8601 minus Z         | x         | "+0100"         | ISO8601 Basic Format with hours and optional minutes |
|                         | xx        | "-0800"         | ISO8601 Basic Format with hours and minutes          |
|                         | xxx       | "+01:00"        | ISO8601 Extended Format with hours and minutes       |
|                         | xxxx      | "+010059"       | ISO8601 Basic Format with hours and minutes, optional seconds     |
|                         | xxxxx     | "+01:00:10"     | ISO8601 Extended Format with hours and minutes, optional seconds  |
| GMT Format              | O         | "+0100"         | Short localised GMT format        |
|                         | OOOO      | "+010059"       | Long localised GMT format         |

## Formatting symbols for hour of day

The hour of day can be formatted differently depending whether
a 12- or 24-hour day is being represented and depending on the
way in which midnight and noon are represented.  The following
table illustrates the differences:

| Symbol  | Midn.	|	Morning	| Noon |	Afternoon	| Midn. |
| :----:  | :---: | :-----: | :--: | :--------: | :---: |
|   h	    |  12	  | 1...11	|  12	 |   1...11   |  12   |
|   K	    |   0	  | 1...11	|   0	 |   1...11   |   0   |
|   H	    |   0	  | 1...11	|  12	 |  13...23   |   0   |
|   k	    |  24	  | 1...11	|  12	 |  13...23   |  24   |

## Relative Date, Time and DateTime Localization Formatting

The primary API for formatting relative dates and datetimes is `MyApp.Cldr.DateTime.Relative.to_string/2`.  Some examples:

```elixir
iex> MyApp.Cldr.DateTime.Relative.to_string(-1)
{:ok, "1 second ago"}

iex> MyApp.Cldr.DateTime.Relative.to_string(1)
{:ok, "in 1 second"}

iex> MyApp.Cldr.DateTime.Relative.to_string(1, unit: :day)
{:ok, "tomorrow"}

iex> MyApp.Cldr.DateTime.Relative.to_string(1, unit: :day, locale: "fr")
{:ok, "demain"}

iex> MyApp.Cldr.DateTime.Relative.to_string(1, unit: :day, format: :narrow)
{:ok, "tomorrow"}

iex> MyApp.Cldr.DateTime.Relative.to_string(1234, unit: :year)
{:ok, "in 1,234 years"}

iex> MyApp.Cldr.DateTime.Relative.to_string(1234, unit: :year, locale: "fr")
{:ok, "dans 1 234 ans"}

iex> MyApp.Cldr.DateTime.Relative.to_string(31)
{:ok, "in 31 seconds"}

iex> MyApp.Cldr.DateTime.Relative.to_string(~D[2017-04-29], relative_to: ~D[2017-04-26])
{:ok, "in 3 days"}

iex> MyApp.Cldr.DateTime.Relative.to_string(310, format: :short, locale: "fr")
{:ok, "dans 5 min"}

iex> MyApp.Cldr.DateTime.Relative.to_string(310, format: :narrow, locale: "fr")
{:ok, "+5 min"}

iex> MyApp.Cldr.DateTime.Relative.to_string 2, unit: :wed, format: :short
{:ok, "in 2 Wed."}

iex> MyApp.Cldr.DateTime.Relative.to_string 1, unit: :wed, format: :short
{:ok, "next Wed."}

iex> MyApp.Cldr.DateTime.Relative.to_string -1, unit: :wed, format: :short
{:ok, "last Wed."}

iex> MyApp.Cldr.DateTime.Relative.to_string -1, unit: :wed
{:ok, "last Wednesday"}

iex> MyApp.Cldr.DateTime.Relative.to_string -1, unit: :quarter
{:ok, "last quarter"}

iex> MyApp.Cldr.DateTime.Relative.to_string -1, unit: :mon, locale: "fr"
{:ok, "lundi dernier"}

iex> MyApp.Cldr.DateTime.Relative.to_string(~D[2017-04-29], unit: :ziggeraut)
{:error, {Cldr.UnknownTimeUnit,
"Unknown time unit :ziggeraut.  Valid time units are [:day, :hour, :minute, :month, :second, :week, :year, :mon, :tue, :wed, :thu, :fri, :sat, :sun, :quarter]"}}
```

## Interval Formatting

Interval formats allow for software to format intervals like "Jan 10-12, 2008" as a shorter and more natural format than "Jan 10, 2008 - Jan 12, 2008". They are designed to take a start and end date, time or datetime plus a formatting pattern and use that information to produce a localized format.

An interval is expressed as either a `from` and `to` date, time or datetime. Or it can also be a `Date.Range` or `CalendarInterval` from the [calendar_interval](https://hex.pm/packages/calendar_interval) library.

`Cldr.Interval.to_string/3` function to format an interval based upon the type of the arguments: date, datetime or time.  The modules `Cldr.Date.Interval`, `Cldr.Time.Interval` and `Cldr.DateTime.Interval` also provide a `to_string/3` function for when the desired output format is more specific.

Some examples:

```elixir
iex> Cldr.Interval.to_string ~D[2020-01-01], ~D[2020-12-31], MyApp.Cldr
{:ok, "Jan 1 – Dec 31, 2020"}

iex> Cldr.Interval.to_string ~D[2020-01-01], ~D[2020-01-12], MyApp.Cldr
{:ok, "Jan 1 – 12, 2020"}

iex> Cldr.Interval.to_string ~D[2020-01-01], ~D[2020-01-12], MyApp.Cldr,
...> format: :long
{:ok, "Wed, Jan 1 – Sun, Jan 12, 2020"}

iex> Cldr.Interval.to_string ~D[2020-01-01], ~D[2020-12-01], MyApp.Cldr,
...> format: :long, style: :year_and_month
{:ok, "January – December 2020"}

iex> use CalendarInterval
iex> Cldr.Interval.to_string ~I"2020-01-01/12", MyApp.Cldr,
...> format: :long
{:ok, "Wed, Jan 1 – Sun, Jan 12, 2020"}

iex> Cldr.Interval.to_string ~U[2020-01-01 00:00:00.0Z], ~U[2020-12-01 10:05:00.0Z], MyApp.Cldr,
...> format: :long
{:ok, "January 1, 2020 at 12:00:00 AM UTC – December 1, 2020 at 10:05:00 AM UTC"}

iex> Cldr.Interval.to_string ~U[2020-01-01 00:00:00.0Z], ~U[2020-01-01 10:05:00.0Z], MyApp.Cldr,
...> format: :long
{:ok, "January 1, 2020 at 12:00:00 AM UTC – 10:05:00 AM UTC"}
```

## Known restrictions and limitations

Although largely complete (with respect to the CLDR data), there are some known limitations as of release 2.0.

* *Timezones*  Although the timezone format codes are supported (formatting symbols `v`, `V`, `x`, `X`, `z`, `Z`, `O`) not all localisations are performed.  Only that data available within a `t:DateTime.t/0` struct is used to format timezone data.