Localize.DateTime (Localize v1.4.0)

Copy Markdown View Source

Provides localized formatting of DateTime, NaiveDateTime, and datetime-like maps.

The primary function is to_string/2 which accepts a datetime value and an options keyword list. Format patterns are defined in CLDR and described in TR35.

Ordinal days are a technical preview

The ddd field formats the day as a date ordinal ("Jul 6th"). TR35 designates the section a technical preview, so both the output and the surface may change. Localize.Date documents the detail.

Predefined formats

  • :short — abbreviated date and time (e.g., "1/2/25, 3:04 PM").

  • :medium — standard date and time (default).

  • :long — includes time zone name.

  • :full — verbose day-of-week, date, and time zone.

Custom CLDR skeleton strings and raw format patterns are also supported via the :format option.

Summary

Functions

Returns the numeric date and time separators a locale uses.

Parses a localized date and time string.

Formats a datetime into typed parts, mirroring ECMA-402's formatToParts.

Same as to_parts/2 but raises on error.

Formats a datetime according to a CLDR format pattern.

Same as to_string/2 but raises on error.

Functions

numeric_separators(locale \\ Localize.get_locale(), calendar_type \\ :gregorian)

@spec numeric_separators(Localize.locale(), atom()) ::
  {:ok, map()} | {:error, Exception.t()}

Returns the numeric date and time separators a locale uses.

These are CLDR 49's numericDateSeparator and numericTimeSeparator, which TR35 designates a technical preview — the data and the behaviour may change. TR35 offers them so an implementation can let a caller choose a separator — dates as "05/06/2006" or "05-06-2006", times as "23:59" or "23.59" — which to_string/2 does through its :numeric_date_separator and :numeric_time_separator options. Read them to show a caller what the locale uses before they override it.

Arguments

Returns

  • {:ok, separators} where separators is a map with the keys :numeric_date_separator and :numeric_time_separator.

  • {:error, exception} if the locale or calendar is unknown, or the locale carries no separator data.

Examples

iex> Localize.DateTime.numeric_separators(:en)
{:ok, %{numeric_date_separator: "/", numeric_time_separator: ":"}}

iex> Localize.DateTime.numeric_separators(:fi)
{:ok, %{numeric_date_separator: ".", numeric_time_separator: "."}}

parse(string, options \\ [])

@spec parse(String.t(), Keyword.t()) ::
  {:ok, NaiveDateTime.t() | DateTime.t()} | {:error, Exception.t()}

Parses a localized date and time string.

Accepts any shape the locale accepts, including the locale's CLDR short, medium, long and full patterns and ISO 8601.

ISO 8601 is read in every form it joins a date to a time with a T: the date as a calendar date, a day of the year ("2026-167") or a week date ("2026-W25-2"), the time with its seconds, or its minutes and seconds, left out ("2026-06-16T10:30"), and either without its separators ("20260616T103000"). A fraction is the second's alone: a fraction of a minute or of an hour is not read.

An input carrying a fixed UTC offset resolves to a DateTime.t/0, whether written as an ISO 8601 offset (+05:30, Z) or in the locale's GMT format (GMT+10:30, UTC-5). Its time zone is the offset itself, "+05:30", as ECMA-262 Temporal and RFC 9557 name the zone of a fixed offset, and "Etc/UTC" for no offset at all; see Localize.TimeZoneDatabase for the functions of DateTime that look such a zone up. A named zone (PST, Asia/Tokyo) carries no offset of its own and needs a time-zone database, which the companion calendrical package supplies; without it the zone is dropped and the result is a NaiveDateTime.t/0.

Arguments

  • string is a string in any shape the locale accepts, including the locale's CLDR short, medium, long and full patterns and ISO 8601.

  • options is a keyword list of options.

Options

  • :locale is a locale identifier. The default is the locale returned by Localize.get_locale/0.

  • :calendar is a calendar module, such as Calendar.ISO (the default), Calendrical.Gregorian or Calendrical.Hebrew. The input is read with the locale's patterns for the calendar's CLDR type, and the date is built and returned in this module. A week date, in any form ISO 8601 writes one in ("2024-W05-4", "2024W054"), is the calendar's own, as its date_from_week_date/3 answers: ISO 8601's week for Calendar.ISO, and the fifth week of its own year for a calendar built of weeks. Text that none of the calendar's own formats reads is read in the calendar its info/0 names as :parsing_calendar, and converted, as Localize.Date.parse/2 reads a date. A calendar's own formats come before ISO 8601: a year, a month and a day between hyphens that any of them reads, as CLDR's root short date of the Chinese calendar does (r-MM-dd), and a time after a space are the calendar's own, and where none does, or with ISO 8601's T, the text is an ISO 8601 date and time, converted into the calendar. Anything that is not a calendar module, including a CLDR calendar name such as :hebrew or "gregorian", and a module that implements only the Calendar behaviour, returns a Localize.UnknownCalendarError.t/0.

  • :format is the format the date and time were written with, as to_string/2 takes it: a standard format (:short, :medium, :long or :full), a skeleton atom such as :yMdHm, a Localize.DateTime.SemanticSkeleton, or a pattern string such as "d/M/y HH:mm". Each half is read with its part of it and with nothing else, ISO 8601 included. A standard format is the date's and the time's alike. A skeleton is split into its date fields and its time fields, and so is a semantic skeleton, its zone going with its time. A pattern, which writes its date fields in one run and its time fields in another, is split at the text between them, where the input is split too. The default is nil: the text is read as ISO 8601 or in whichever of the locale's formats reads it first.

  • :date_format and :time_format are the formats the date and the time were written with, each as Localize.Date.parse/2 and Localize.Time.parse/2 take :format, and take the place of the halves of :format. A half with no format is read in any of the locale's formats.

  • :reference_date is the Date.t/0 that partial input is completed against, taken in the calendar the input is read in. The default is today.

  • :as is :struct or :map. :map returns only the fields the input actually carried, rather than completing them; a field no date or time has is an error in both forms. A zone adds the DateTime.t/0 zone fields (:time_zone, :utc_offset, :std_offset and :zone_abbr) as the struct form resolves them: a fixed offset always, and a named zone when the input gives the full date its offset depends on; otherwise :time_zone holds the zone as written. A date and time read in another calendar and converted, as a calendar of weeks reads one, comes back whole, but for a week written without its day, which is the calendar's year and week. The default is :struct.

Returns

Examples

iex> Localize.DateTime.parse("22.03.2026, 14:30", locale: :de)
{:ok, ~N[2026-03-22 14:30:00]}

iex> Localize.DateTime.parse("March 22, 2026, 2:30 PM", locale: :en)
{:ok, ~N[2026-03-22 14:30:00]}

iex> Localize.DateTime.parse("2026-W12-7T14:30", locale: :en)
{:ok, ~N[2026-03-22 14:30:00]}

iex> {:ok, map} = Localize.DateTime.parse("March 22, 2026, 2:30 PM GMT+5", locale: :en, as: :map)
iex> Map.take(map, [:hour, :time_zone, :utc_offset])
%{hour: 14, time_zone: "+05:00", utc_offset: 18000}

to_parts(datetime, options \\ [])

@spec to_parts(map(), Keyword.t()) ::
  {:ok, [%{type: atom(), value: String.t()}]} | {:error, Exception.t()}

Formats a datetime into typed parts, mirroring ECMA-402's formatToParts.

The parts concatenate to exactly the string to_string/2 produces with the same options. Each pattern field is tagged with its type (:year, :month, :day, :weekday, :hour, :minute, :second, :day_period, :time_zone_name, :era, :fractional_second, :literal, …). Standard formats, skeleton atoms, explicit pattern strings, and combined date+time wrappers all decompose.

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.

  • {:error, exception} if the datetime cannot be formatted.

Examples

iex> Localize.DateTime.to_parts(~N[2017-07-10 14:30:00], format: :hm, locale: :en, prefer: :ascii)
{:ok,
 [
   %{type: :hour, value: "2"},
   %{type: :literal, value: ":"},
   %{type: :minute, value: "30"},
   %{type: :literal, value: " "},
   %{type: :day_period, value: "PM"}
 ]}

to_parts!(datetime, options \\ [])

@spec to_parts!(map(), 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 the datetime cannot be formatted.

Examples

iex> Localize.DateTime.to_parts!(~N[2017-07-10 14:30:00], format: :hm, locale: :en, prefer: :ascii) |> length()
5

to_string(datetime, options \\ [])

@spec to_string(map(), Keyword.t()) :: {:ok, String.t()} | {:error, Exception.t()}

Formats a datetime according to a CLDR format pattern.

Arguments

  • datetime is a DateTime.t/0, NaiveDateTime.t/0, or any map with date or time keys. A map holding only some of the date and time fields formats its date half and its time half separately and joins them; one holding only date keys or only time keys is formatted as Localize.Date or Localize.Time would format it.

  • options is a keyword list of options.

Options

  • :format is a standard format name (:short, :medium, :long, :full), a format skeleton atom, a Localize.DateTime.SemanticSkeleton or a format pattern string. The default is :medium. A standard format sets the width of the date and the time together; :date_format and :time_format override each axis separately. A value with no time zone, a NaiveDateTime.t/0 or a map holding neither :time_zone nor :utc_offset, is written at :long and :full with the time Localize.Time.to_string/2 writes for it, the format's fields without its zone.

  • :date_format and :time_format are standard format names, skeleton atoms or semantic skeletons that set the date half and the time half independently, each defaulting to :format. Use them for the common "full date, short time" pairing: date_format: :full, time_format: :short renders "Wednesday, April 8, 2026, 12:00 PM". When :date_format is given it also selects the wrapper width.

  • :style selects the CLDR pattern that joins the date and the time. :at (the default) uses the locale's "at time" wrapper ("April 8, 2026 at 12:00:00 PM", de "8. April 2026 um 12:00:00"); :default uses the standard wrapper ("April 8, 2026, 12:00:00 PM").

    TR35 makes the "at time" wrapper the default for a single date combined with a single time — an event time — and asks that an API also offer the standard wrapper for a current time, which is what :default is. CLDR defines the "at time" wrapper only for :full and :long, so both styles agree at :medium and :short.

  • :locale is a locale identifier. The default is :en.

  • :number_system is a CLDR numbering system name (for example, :thai). All numeric fields render in that system; a -u-nu- locale extension may be used instead. The default is the locale's number system.

  • :numeric_date_separator is a string replacing the locale's numericDateSeparator wherever it separates the fields of a numeric-month date, as TR35 allows an implementation to offer. Localize.DateTime.numeric_separators/2 returns the locale's own. The default is the locale's separator.

  • :numeric_time_separator is a string replacing the locale's numericTimeSeparator wherever it separates time fields. The default is the locale's separator.

  • :prefer selects between CLDR alt variants. Accepts an atom or a list of atoms in priority order. Recognised values: :standard / :variant (locales like en-CA publish both an ISO pattern "y-MM-dd" and a locale-variant "d/M/yy"), and :unicode / :ascii (mostly time formats — NBSP and curly quotes vs ASCII-only). Examples: prefer: :variant, prefer: [:variant, :ascii]. The default is [:standard, :unicode].

Returns

  • {:ok, formatted_string} on success.

  • {:error, exception} if the datetime cannot be formatted.

Examples

iex> Localize.DateTime.to_string(~N[2017-07-10 14:30:00], locale: :en, prefer: :ascii)
{:ok, "Jul 10, 2017, 2:30:00 PM"}

iex> Localize.DateTime.to_string(~N[2017-07-10 14:30:00], format: :short, locale: :en, prefer: :ascii)
{:ok, "7/10/17, 2:30 PM"}

iex> Localize.DateTime.to_string(%{year: 2026, month: 6, day: 15, hour: 14}, locale: :en, prefer: :ascii)
{:ok, "Jun 15, 2026, 2 PM"}

to_string!(datetime, options \\ [])

@spec to_string!(map(), Keyword.t()) :: String.t()

Same as to_string/2 but raises on error.

Arguments

Options

See to_string/2 for the supported options.

Returns

  • A formatted string.

  • Raises an exception if the datetime cannot be formatted.

Examples

iex> Localize.DateTime.to_string!(~N[2017-07-10 14:30:00], locale: :en, prefer: :ascii)
"Jul 10, 2017, 2:30:00 PM"

iex> Localize.DateTime.to_string!(~N[2017-07-10 14:30:00], format: :short, locale: :en, prefer: :ascii)
"7/10/17, 2:30 PM"