kip
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.
Links
Trending in Announcing
Other Trending Topics
Categories:
Sub Categories:
Forums
Popular Tags
- #ecto
- #liveview
- #troubleshooting
- #learning-elixir
- #library
- #deployment
- #erlang
- #testing
- #genserver
- #mix
- #absinthe
- #remote-other
- #otp
- #plug
- #how-to-question
- #macros
- #postgres
- #elixirconf
- #channels
- #exunit
- #discussion
- #code-sync
- #podcasts
- #javascript
- #onsite
- #dialyzer
- #docker
- #authentication
- #umbrella
- #full-time-contract
- #podcasts-by-brainlid
- #ecto-query
- #elixirconf-us
- #ai
- #blog-post
- #elixir-ls
- #phoenix_html
- #iex
- #graphql
- #genstage
- #websockets
- #supervisor
- #advent-of-code
- #distillery
- #processes
- #api
- #forms
- #metaprogramming
- #hex
- #security










Showing Posts 1 to 6- Show Best Posts
- Show All (oldest first)
- Show All (newest first)
netoum
Thank you for providing all the Localize libraries. Amazing job, they work great and make the dev life much easier.
Just noticed the GitHub link in the post is not working however the GitHub link in the hex package is working.
kip
Looks like @AstonJ (thanks!) already beat me to it - the link is correct now.
dbern
Wonderful! Maybe I can deprecate date_time_parser! It doesn’t support locales very well and I’m glad Calendrical does.
kip
TL;DR: your lib remains incredibly useful, Calendrical covers a distinct set of requirements - but definitely not all of them.
There are a number of use cases that date_time_parser supports that
Calendricaldoes not and I consider out of scope.-9999999999,30134,62.0, etc.)""=""9/5/2018""")date(1)output, year at end (Fri Mar 2 09:01:57 2018)Date.toString()(Fri Mar 31 2017 21:41:40 GMT+0000 (UTC))mm:ss.fractime (07:09.3)2017-09-29+00:00)dbern
Fair enough! And yes it’s all those non-standard formats that I remember being problematic in CSV imports that had a variety of these formats that I made date_time_parser for.
in any case, wonderful job with Calendrical!
kip
I’ve published Calendrical version 0.11.0 the is primarily about robustness, conformance and code quality. It’s based upon Localize version 0.45.0 also released today and which is also focus on robustness, conformance and code quality.
The changelog contains notes on all of the changes.
I expect
Calendricalto be a stable release now and the next release will be a 1.0 candidate release, followingLocalizearound the end of July.