TR35 semantic skeletons: asking for a date or time by meaning rather than by which fields to render.
A classical skeleton such as :yMMMd is an instruction — year, abbreviated
month, day. A semantic skeleton is a request: "year, month, day and
weekday, at medium length", written YMDE. The library then chooses the
fields, and a locale is free to choose differently.
CLDR ships no semantic skeleton data. TR35 §Mapping to Standard Skeletons
resolves one from data Localize already has: the year, month and day take
their widths from the locale's own date format at the requested length,
and the weekday, time and zone come from a fixed table. So a medium date is
yMMMd in en but yMMdd in de, whose medium date is numeric. The
datetime conformance data, the one file in CLDR that mentions semantic
skeletons, pairs each of its cases with the classical skeleton it resolves
to, and every case resolves as it says.
Semantic skeletons are a technical preview
TR35 designates semantic skeletons, and the MessageFormat 2 date and time functions built on them, a technical preview. The fields a request resolves to may change with a later CLDR release.
Field codes
The skeleton code names the fields wanted:
Y— yearM— monthD— day of the monthE— day of the weekT— timeZ— time zone
So YMD is a plain date, YMDE adds the weekday, T is a time on its
own, and MDTZ is a month, day, time and zone together.
The codes name a set, so their order does not matter, and TR35 defines
which sets there are: a date — D, E, DE, MD, MDE, YMD or
YMDE; a calendar period — Y, M or YM; a time, T; and a zone,
Z. A date may be joined to a time, a zone or both (YMDT, MDZ,
ETZ) and a time to a zone (TZ), but a calendar period stands alone.
Any other set, such as YD or YMT, is an error, as TR35 requires.
Usage
import Localize.DateTime.SemanticSkeleton, only: [semantic: 1, semantic: 2]
Localize.DateTime.to_string(datetime, format: semantic("YMDE"))
Localize.DateTime.to_string(datetime,
format: semantic("MDTZ", length: :long, zone_style: :generic))The struct is accepted anywhere :format is, alongside the standard
styles, classical skeletons and literal patterns it already took. Building
it once and reusing it across calls is cheaper than re-validating a
keyword list each time.
Summary
Functions
Builds a semantic skeleton.
Builds a semantic skeleton, raising on invalid input.
Returns the classical skeleton a semantic skeleton resolves to.
Types
@type field() :: :year | :month | :day | :weekday | :time | :zone
@type t() :: %Localize.DateTime.SemanticSkeleton{ alignment: :inline | :column, fields: [field()], hour_cycle: :auto | :clock12 | :clock24 | :h11 | :h12 | :h23 | :h24, length: :short | :medium | :long, time_precision: time_precision(), year_style: :auto | :full | :with_era, zone_length: :auto | :short | :long, zone_style: :specific | :generic | :location | :offset }
@type time_precision() ::
:hour | :minute | :minute_optional | :second | {:fractional_second, 1..9}
Functions
@spec new(String.t() | [field()], Keyword.t()) :: {:ok, t()} | {:error, Exception.t()}
Builds a semantic skeleton.
Arguments
codeis a string of field codes such as"YMDE"or"MDTZ", or a list of field atoms such as[:year, :month, :day].optionsis a keyword list.
Options
:lengthis:short,:medium(the default) or:long. It says how much space the result has, and the locale's own date format at that length sets the widths of the year, month and day —:shortis usually numeric,:longspells the month out.:year_styleis:auto(the default),:fullor:with_era.:autotakes the year as the locale's date format writes it, era and all;:fullnever truncates it to two digits;:with_eraalso shows the era.:zone_styleis:specific(the default),:generic,:locationor:offset.:zone_lengthis:auto(the default),:shortor:long.:autois TR35's rule: a zone on its own takes the long form except at short length, and a zone beside other fields the short one.:shortand:longchoose the form outright —zorzzzzfor a specific zone,vorvvvvfor a generic one,OorOOOOfor an offset — which TR35 does not provide for but MessageFormat 2'stimeZoneStyleneeds. A location zone has one form.:hour_cycleis:auto(the default),:clock12,:clock24,:h11,:h12,:h23or:h24, in TR35's vocabulary.:autouses the locale's own cycle, which is what a semantic request usually means.:clock12and:clock24ask for a 12- or 24-hour clock and then take the locale's pattern as it stands, sojarendersKrather thanh. The four exact cycles substitute their hour symbol into the matched pattern —:h11K,:h12h,:h23H,:h24k— which is the only way to be sure which symbol you get.:time_precisionis:hour,:minute,:minute_optional,:second(the default) or{:fractional_second, digits}withdigitsfrom 1 to:minute_optionaldrops the minutes when they are zero.
:alignmentis:inline(the default) or:column, TR35's two alignments.:columnis for dates and times set in a column, where equal widths line up: it pads a numeric month, day and hour to two digits, soen's short date "1/5/25" becomes "01/05/25". It never turns a number into text or back, and it acts on the pattern the skeleton resolves to, leaving the classical skeleton as it is.
Each option shapes certain fields, and TR35 makes giving one for a field
set that has none of them an error: :year_style needs Y, :hour_cycle
and :time_precision need T, :zone_style and :zone_length need Z,
and :alignment needs Y, M, D or T. :length suits every set.
Returns
{:ok, skeleton}.{:error, exception}if a field code or option value is unknown, the fields are not a set TR35 defines, or an option is given for a set without the fields it applies to.
Examples
iex> {:ok, skeleton} = Localize.DateTime.SemanticSkeleton.new("YMDE")
iex> skeleton.fields
[:year, :month, :day, :weekday]
iex> Localize.DateTime.SemanticSkeleton.new("YMDQ")
{:error, %Localize.InvalidValueError{value: "Q", expected: "one of Y, M, D, E, T, Z", context: "Localize.DateTime.SemanticSkeleton"}}
Builds a semantic skeleton, raising on invalid input.
Intended for the :format option, where the skeleton is usually a literal
and a mistake in it is a programming error rather than bad data.
Arguments
- See
new/2.
Returns
- A
t/0.
Raises
Localize.InvalidValueErrorwherevernew/2returns an error.
Examples
iex> skeleton = Localize.DateTime.SemanticSkeleton.semantic("YMD", length: :long)
iex> {skeleton.fields, skeleton.length}
{[:year, :month, :day], :long}
@spec to_classical_skeleton(t(), Keyword.t()) :: {:ok, atom()} | {:error, Exception.t()}
Returns the classical skeleton a semantic skeleton resolves to.
This is TR35 §Mapping to Standard Skeletons: the year, month and day take
their widths from the locale's date format at the skeleton's length, the
weekday, time and zone come from a fixed table, and the time precision and
year style then adjust the result. The result is an ordinary skeleton
atom, resolved from there by the machinery that already handles
format: :yMMMd.
Arguments
skeletonis at/0.optionsis a keyword list.
Options
:localeis the locale whose date formats set the widths of the year, month and day. The default isLocalize.get_locale/0.:calendaris a calendar module,Calendar.ISOby default. Its CLDR calendar type selects the date formats, so a year in a calendar that counts years by era carries one.:valueis the date or time being formatted. Only:minute_optionalprecision consults it, dropping the minutes when they are zero; without a value the minutes are shown.
Returns
{:ok, atom}— the classical skeleton.{:error, exception}if the skeleton is not onenew/2builds, as a struct made by hand may not be, or the locale or calendar is not known.
Examples
iex> skeleton = Localize.DateTime.SemanticSkeleton.semantic("YMDE", length: :short)
iex> Localize.DateTime.SemanticSkeleton.to_classical_skeleton(skeleton, locale: :en)
{:ok, :yyMdEEE}
iex> skeleton = Localize.DateTime.SemanticSkeleton.semantic("YMD")
iex> Localize.DateTime.SemanticSkeleton.to_classical_skeleton(skeleton, locale: :de)
{:ok, :yMMdd}
iex> skeleton = Localize.DateTime.SemanticSkeleton.semantic("T", time_precision: :minute)
iex> Localize.DateTime.SemanticSkeleton.to_classical_skeleton(skeleton, locale: :en)
{:ok, :jm}