Somehow I missed introducing Calendrical, which implements a significant number of the calendars used around the world. Its the spiritual successor to ex_cldr_calendars and friends, but it also several additional calendars like:
- Buddhist
- Hebrew
- Islamic - Civil, Observational, Rgsa, Tbla and Um Al Qura (including a version based upon the published tables and a version based upon astronomical calculation)
- Indian
New Date, Time, DateTime and Interval string parsers
New in Calendarical version 0.6.0 published today is flexible date/time parsing.
If you’ve got user-typed date/time strings coming in from a form, an import, or a chat command, this should save you some pain.
There’s a single entry point — Calendrical.parse/2 — that figures out what kind of value you typed and dispatches to the right sub-parser:
iex> Calendrical.parse("2026-05-16", locale: :en)
{:ok, ~D[2026-05-16]}
iex> Calendrical.parse "23 May"
{:ok, ~D[2026-05-23]}
iex> Calendrical.parse("14:30", locale: :en)
{:ok, ~T[14:30:00]}
iex> Calendrical.parse("May 16, 2026, 2:30 PM", locale: :en)
{:ok, ~N[2026-05-16 14:30:00]}
iex> Calendrical.parse("May 5, 2026 – May 10, 2026", locale: :en)
{:ok, Date.range(~D[2026-05-05], ~D[2026-05-10])}
Parsing is locale aware
Every parser reads CLDR data, so the same string may parse differently depending on the locale you pass:
iex> Calendrical.Date.parse("3/4/26", locale: :en)
{:ok, ~D[2026-03-04]}
iex> Calendrical.Date.parse("3/4/26", locale: :"en-GB")
{:ok, ~D[2026-04-03]}
iex> Calendrical.Date.parse("16.05.2026", locale: :de)
{:ok, ~D[2026-05-16]}
iex> Calendrical.parse "23 mai", locale: :fr
{:ok, ~D[2026-05-23]}
iex> Calendrical.Time.parse("2:30 PM", locale: :en)
{:ok, ~T[14:30:00]}
iex> Calendrical.Time.parse("14:30", locale: :de)
{:ok, ~T[14:30:00]}
Parsing supports any known calendar
Pass :calendar to interpret input pretty much all of the worlds major calendars — Gregorian, Buddhist, Japanese imperial, Islamic, Hebrew, Persian, ROC, Coptic, Ethiopic, Indian, and more. The returned Date is in that calendar:
iex> Calendrical.Date.parse("2026-05-16", calendar: Calendrical.Hebrew)
{:ok, ~D[5786-09-29 Calendrical.Hebrew]}
iex> Calendrical.Date.parse("民國115年5月16日", locale: :"zh-Hant-TW", calendar: Calendrical.Roc)
{:ok, ~D[0115-05-16 Calendrical.Roc]}
iex> Calendrical.Date.parse("令和6年7月1日", locale: :"ja-JP", calendar: Calendrical.Japanese)
{:ok, ~D[2024-07-01 Calendrical.Japanese]}
iex> Calendrical.Date.parse("١٧ رمضان ١٤٣٥ هـ", locale: :"ar-SA", calendar: Calendrical.Islamic.Civil)
{:ok, ~D[1435-09-17 Calendrical.Islamic.Civil]}
iex> Calendrical.Date.parse("1 ก.ค. 2567", locale: :"th-TH", calendar: Calendrical.Buddhist)
{:ok, ~D[2567-07-01 Calendrical.Buddhist]}
Parsing date ranges
Date ranges follow the same calendar rule — Date.Range works in any calendar, not just Calendar.ISO:
iex> Calendrical.Date.parse_range({"2026-05-05", "2026-05-10"}, calendar: Calendrical.Buddhist])
{:ok, Date.range(~D[2569-05-05 Calendrical.Buddhist], ~D[2569-05-10 Calendrical.Buddhist])}
iex> Calendrical.Date.parse_range("May 5 – May 10, 2026", locale: :en)
{:ok, Date.range(~D[2026-05-05], ~D[2026-05-10])}
That last example also shows CLDR interval-skeleton inheritance — the left endpoint has no year, so it inherits 2026 from the right.
Going directly to the individual parsers
If you already know the shape of your input, skip Calendrical.parse/2 and call the relevant parser directly:
Calendrical.Date.parse/2Calendrical.Time.parse/2Calendrical.DateTime.parse/2Calendrical.Date.parse_range/2
Each accepts :locale, :calendar (where applicable), and a few specialised options like :allow_inverted for ranges and :reference_date for two-digit-year pivoting.






















