Date and Time Localization and Formatting
View SourceInstallation
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"}
]
endthen retrieve ex_cldr_dates_times from hex:
mix deps.get
mix deps.compileConfiguring a required backend module
ex_cldr_dates_times uses the configuration set for the dependency ex_cldr. See the documentation for ex_cldr.
A backend module is required that is used to host the functions that manage CLDR data. An example to get started is:
Create a backend module (see ex_cldr for details of the available options). Note the requirement to configure the appropriate
Cldrprovider backends.defmodule MyApp.Cldr do use Cldr, locales: ["en", "fr", "ja"], providers: [Cldr.Number, Cldr.Calendar, Cldr.DateTime] end[Optional] Update
config.exsconfiguration to specify this backend as the system default. Not required, but often useful.config :ex_cldr, default_locale: "en", default_backend: MyApp.Cldr
Introduction
ex_cldr_dates_times is an addon library application for 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:
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:
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_stringDate, 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/2are:short,:medium,:longand:full. The default is:medium. For example, assuming a configured backend calledMyApp.Cldr: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:
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 isMyApp.Cldr). The set of common format names across all locales configured inex_cldrcan be returned byMyApp.Cldr.DateTime.Format.common_date_time_format_names/0.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
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.
| 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 |
| "02" | Two digits | ||
| QQQ | "Q2" | Abbreviated | |
| QQQQ | "2nd quarter" | Wide | |
| QQQQQ | "2" | Narrow | |
| Standalone Quarter | q | 2 | Single digit |
| "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:
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 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:
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 aDateTime.t/0struct is used to format timezone data.