Skip to content

Calendrical version 0.6.0

Choose a tag to compare

@kipcole9 kipcole9 released this 22 May 22:33
· 12 commits to main since this release

[0.6.0] — 2026-05-23

Breaking changes

  • Calendrical.DateParseError, TimeParseError, DateTimeParseError, DateRangeParseError, and ParseError no longer carry a :message struct field. The human-readable message is materialised by Exception.message/1 from the semantic fields (:input, :locale, :calendar, :reason, :from, :to, :cause, :attempts). Pattern-match on :reason (and other structural fields) rather than parsing the rendered string. DateRangeParseError now declares @behaviour Localize.Exception and exposes reason_atoms/0 for the closed set of failure categories; the :inverted reason carries :from/:to Date endpoints instead of stuffing them into :input.

Bug Fixes

  • Calendrical.Date.parse_range/2 now returns a Date.Range whose endpoints are in the calendar named by the :calendar option (matching parse/2), instead of always returning Calendar.ISO endpoints. Date.Range supports any calendar provided both endpoints share it, so non-ISO ranges are well-formed.

  • Month, day, era, quarter, and day-period name matching is now case-insensitive per CLDR TR35 §6.5 (Lenient Parsing). Previously "23 Mai" (capitalised) failed to parse in French because the parser case-sensitively matched the lowercase CLDR form "mai"; "23 mai" worked. All four parsers now accept any case for locale name fields.

  • A literal space in a CLDR date pattern now requires at least one whitespace character in the input (previously zero-or-more). Inter-field gaps with no explicit pattern separator stay optional. This prevents over-greedy matches like "mai23" binding to a MMMM d y pattern as month=mai, day=2, year=3.

Added

  • Calendrical.parse/2 — unified locale-aware parser that dispatches to the appropriate sub-parser when the input shape is not known up-front. Tries interval, date, time, then datetime, and returns {:ok, value} where value is a Date, Time, NaiveDateTime, DateTime, or Date.Range. Failures return {:error, Calendrical.ParseError.t()} whose :attempts field records each sub-parser tried.

  • The :calendar option on all parsers now accepts either a CLDR calendar key atom (:gregorian, :hebrew, …) or a calendar module (Calendar.ISO, Calendrical.Hebrew, …). Modules are coerced via the cldr_calendar_type/0 callback; Calendar.ISO is treated as :gregorian.

  • Calendrical.Date.parse/2 now accepts month-name + day input in either order regardless of the locale's preferred ordering. For any CLDR pattern with a name-form month (MMM/MMMM/MMMMM) and a numeric day, the parser also tries the reversed token order — so "May 23" parses in French (CLDR has d MMM) and "23 May" parses in English (CLDR has MMM d, y). Numeric M/MM are excluded because the swap would be ambiguous with d. Applies to year-bearing and weekday-bearing variants too; non-M-and-d tokens stay in place.

  • ISO 8601 forms beyond Elixir stdlib are now accepted: basic format (20260523), ordinal date (2026-143), and ISO week date (2026-W21-6). Calendrical.Date.parse/2 recognises all three in every locale as a universal escape hatch alongside the existing extended format (2026-05-23).

  • Calendrical.DateTime.parse/2 now accepts a space separator between date and time ("2026-05-23 14:30:00") in addition to T. Elixir stdlib's NaiveDateTime.from_iso8601/1 has accepted this form since 1.4; the gate has been relaxed so Calendrical does too. Common in SQL output, log lines, and human-readable timestamps.

  • New parsing guide (guides/parsing.md) describing what each parser accepts, how Calendrical compares to Elixir stdlib, ISO 8601 coverage, and the documented variances from CLDR (case-insensitive name matching, M↔d swap, lenient separators).