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:
mix deps.get
mix deps.compile
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 Cldr
provider backends.
defmodule MyApp.Cldr do
use Cldr,
locales: ["en", "fr", "ja"],
providers: [Cldr.Number, Cldr.Calendar, Cldr.DateTime]
end
[Optional] Update config.exs
configuration to specify this backend as the system default. Not required, but often useful.
config :ex_cldr,
default_locale: "en",
default_backend: MyApp.Cldr
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_string
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
:
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 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
.
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"}
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 |
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 |
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 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"}
Although largely complete (with respect to the CLDR data), there are some known limitations as of release 2.0.
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.