Calendrical. Date
(Calendrical v0.7.1)
Copy Markdown
Date parsing and helpers built on Calendrical's calendar implementations and Localize's CLDR data.
See Calendrical.Date.Parser for the parsing engine details.
When the caller doesn't know in advance whether the input is a
date, time, datetime, or range, use Calendrical.parse/2 — it
dispatches to the appropriate sub-parser.
Summary
Functions
@spec parse(String.t(), Keyword.t()) :: {:ok, Date.t() | map()} | {:error, Exception.t()}
Parses a locale-formatted date string.
Tries, in order: bare ISO-8601 (YYYY-MM-DD), then the
locale's CLDR short/medium/long/full patterns for the
requested calendar. The patterns encode the locale's
preferred field order and any era markers — so the same
input may parse to different dates under different locales
by design.
Returns a Date.t/0 in the calendar identified by the
:calendar option — Calendar.ISO for :gregorian (the
default), Calendrical.Hebrew for :hebrew,
Calendrical.Japanese for :japanese, and so on. Pass
return_calendar: :iso to force the result into Gregorian
regardless (useful for Date.Range, Ecto :date casts, or
any consumer that requires ISO).
Arguments
inputis the raw user input string.optionsis a keyword list of options.
Options
:locale— the locale to interpret the string under. Defaults toLocalize.get_locale/0.:calendar— either a CLDR calendar key (e.g.:gregorian,:buddhist,:islamic_civil,:japanese,:persian,:hebrew) or a calendar module (Calendar.ISO,Calendrical.Hebrew,Calendrical.Buddhist, …). A module is coerced to its CLDR atom via thecldr_calendar_type/0callback;Calendar.ISOis an alias for:gregorian. Defaults to:gregorian. Drives both how the input is interpreted and what calendar the returnedDateis in.:reference_date— the "today" anchor for two-digit-year pivoting. Defaults toDate.utc_today/0.:return_calendar—:native(default) returns the parsed date in whatever:calendarnamed.:isoforcesCalendar.ISO. A calendar module (e.g.Calendrical.Persian) returns the date in that specific calendar.:as—:struct(default) returns aDate.t/0.:mapreturns a bare field map containing only what the input actually supplied (%{month: 5, day: 5, calendar: Calendar.ISO}for"May 5") plus a:calendarkey naming the resolved calendar module. Useful when a downstream library (e.g.Tempo) needs the unresolved partial rather than a defaultedDate. In:mapmode the:reference_datefallback for missing-year inputs is suppressed, and:return_calendarhas no effect (the map stays in the parsing calendar).
Returns
{:ok, Date.t()}on success whenas: :struct(the default).{:ok, map()}on success whenas: :map. The map always has a:calendarkey; the other keys (:year,:month,:day,:quarter,:week_of_year,:week_based_year,:day_of_year,:day_of_week,:day_of_week_in_month) are present only when the input supplied them.{:error, Calendrical.DateParseError.t()}when no pattern matched.
Examples
iex> Calendrical.Date.parse("2026-05-16", locale: :en)
{:ok, ~D[2026-05-16]}
iex> Calendrical.Date.parse("May 5", locale: :en, as: :map)
{:ok, %{calendar: Calendar.ISO, month: 5, day: 5}}
iex> Calendrical.Date.parse("2026", locale: :en, as: :map)
{:ok, %{calendar: Calendar.ISO, year: 2026}}
iex> Calendrical.Date.parse("5/16/26", locale: :en)
{:ok, ~D[2026-05-16]}
iex> Calendrical.Date.parse("16.05.2026", locale: :de)
{:ok, ~D[2026-05-16]}
iex> Calendrical.Date.parse("2026-05-16", locale: :en, calendar: :hebrew)
{:ok, ~D[5786-09-29 Calendrical.Hebrew]}
iex> Calendrical.Date.parse("2026-05-16", locale: :en, calendar: Calendrical.Hebrew)
{:ok, ~D[5786-09-29 Calendrical.Hebrew]}
iex> Calendrical.Date.parse("2026-05-16", locale: :en, calendar: :hebrew, return_calendar: :iso)
{:ok, ~D[2026-05-16]}
iex> Calendrical.Date.parse("Q2 2026", locale: :en)
{:ok, ~D[2026-04-01]}
iex> Calendrical.Date.parse("2nd quarter 2026", locale: :en)
{:ok, ~D[2026-04-01]}
iex> Calendrical.Date.parse("week 20 of 2026", locale: :en)
{:ok, ~D[2026-05-11]}
iex> Calendrical.Date.parse("Saturday, May 16, 2026", locale: :en)
{:ok, ~D[2026-05-16]}
@spec parse_range(String.t() | {String.t(), String.t()}, Keyword.t()) :: {:ok, Date.Range.t() | {map(), map()}} | {:error, Exception.t()}
Parses a locale-formatted date range.
Accepts either a single string (e.g. "May 5 – May 10, 2026")
in which case the parser splits on the locale's CLDR
intervalFormatFallback separator, or a 2-tuple
{from_string, to_string} for two-input UIs that already
have the endpoints split.
Each endpoint is parsed independently via parse/2. The
result is always a Date.Range.t/0; the calendar of the
range's endpoints follows the :calendar option (defaults
to :gregorian, i.e. Calendar.ISO). Date.Range supports
any calendar provided both endpoints share it — both
endpoints are parsed under the same option, so the range is
well-formed for Buddhist, Hebrew, Japanese, Persian, and
every other Calendrical-supported calendar.
Arguments
inputis either a binary or a{from_binary, to_binary}tuple.optionsis a keyword list of options.
Options
Same as parse/2: :locale, :calendar, :reference_date,
:as. Plus:
:allow_inverted— whentrue, an end-before-start range is returned as-is (Elixir'sDate.range/3builds a descending range). Whenfalse(the default), an inverted range is rejected with aCalendrical.DateRangeParseError. Only applies whenas: :struct(the default);as: :mapskips the comparison because partial maps may not have enough fields to compare.
Returns
{:ok, Date.Range.t()}on success whenas: :struct. The endpoints' calendar matches the:calendaroption.{:ok, {from_map, to_map}}on success whenas: :map. Each endpoint is a field map; missing fields are inherited from the other endpoint per the CLDR interval convention (so"May 5 – May 10, 2026"yields two maps both carrying:year).{:error, Calendrical.DateParseError.t() | Calendrical.DateRangeParseError.t()}on failure.
Examples
iex> {:ok, range} = Calendrical.Date.parse_range({"2026-05-05", "2026-05-10"})
iex> {range.first, range.last}
{~D[2026-05-05], ~D[2026-05-10]}
iex> Calendrical.Date.parse_range("May 5 – May 10, 2026", locale: :en, as: :map)
{:ok,
{%{calendar: Calendar.ISO, year: 2026, month: 5, day: 5},
%{calendar: Calendar.ISO, year: 2026, month: 5, day: 10}}}
iex> {:ok, range} = Calendrical.Date.parse_range("May 5, 2026 – May 10, 2026", locale: :en)
iex> {range.first, range.last}
{~D[2026-05-05], ~D[2026-05-10]}
iex> {:ok, range} = Calendrical.Date.parse_range({"2026-05-05", "2026-05-10"}, calendar: :buddhist)
iex> {range.first, range.last}
{~D[2569-05-05 Calendrical.Buddhist], ~D[2569-05-10 Calendrical.Buddhist]}
iex> {:ok, range} = Calendrical.Date.parse_range({"2026-05-05", "2026-05-10"}, calendar: Calendrical.Buddhist)
iex> {range.first, range.last}
{~D[2569-05-05 Calendrical.Buddhist], ~D[2569-05-10 Calendrical.Buddhist]}