Changelog
Copy MarkdownAll notable changes to this project will be documented in this file.
The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.
[0.3.0] — 2026-04-22
Bug Fixes
- Fixes mapping CLDR calendar types to the implementation module name.
[0.2.0] — 2026-04-16
This is the first release of Calendrical, which consolidates the ex_cldr_calendars library family into a single package built on Localize. Functionality from the following libraries has been merged in: ex_cldr_calendars, ex_cldr_calendars_persian, ex_cldr_calendars_coptic, ex_cldr_calendars_ethiopic, ex_cldr_calendars_japanese, ex_cldr_calendars_lunisolar, ex_cldr_calendars_islamic, ex_cldr_calendars_format, and ex_cldr_calendars_composite.
Added
Calendrical.Behaviour— adefmacro __using__template that supplies sensible default implementations of everyCalendarandCalendricalcallback. Calendarsusethe behaviour, supply an:epoch(and any non-default options), definedate_to_iso_days/3anddate_from_iso_days/1, and override only the callbacks that differ from the defaults. Every generated function isdefoverridable. Seeguides/calendar_behaviour.md.All 17 CLDR-acceptable calendar types are implemented:
Calendrical.Gregorian,Calendrical.ISO,Calendrical.ISOWeek,Calendrical.NRF— month- and week-based Gregorian calendars.Calendrical.Julianand the year-start variantsCalendrical.Julian.Jan1,Calendrical.Julian.March1,Calendrical.Julian.March25,Calendrical.Julian.Sept1,Calendrical.Julian.Dec25.Calendrical.Buddhist— Thai Buddhist Era (Gregorian + 543).Calendrical.Roc— Republic of China / Minguo (Gregorian − 1911).Calendrical.Japanese— proleptic Gregorian with Japanese era data for localization.Calendrical.Indian— Indian National (Saka) calendar with custom 30/31-day month structure and Saka era (Gregorian − 78).Calendrical.Persian— astronomical Persian calendar based on the vernal equinox at Tehran, computed viaAstro.equinox/2.Calendrical.CopticandCalendrical.Ethiopic— 13-month tabular calendars sharing themod(year, 4) == 3leap-year rule, with overriddenquarter_of_year/3,day_of_week/4, andvalid_date?/3.Calendrical.Ethiopic.AmeteAlem— Ethiopic calendar with the Era of the World (Anno Mundi) year offset of +5500 over the standard Era of Mercy.Calendrical.Islamic.CivilandCalendrical.Islamic.Tbla— tabular Hijri calendars with the Type II Kūshyār 30-year leap cycle. They share a privateCalendrical.Islamic.Tabularhelper and differ only in epoch (Friday 16 July 622 Julian vs Thursday 15 July 622 Julian).Calendrical.Islamic.UmmAlQura— Saudi Umm al-Qura tabular calendar embedding the official KACST/van Gent first-of-month dataset (1356–1500 AH) at compile time. Conversions are O(1) forward and O(log n) reverse via binary search.Calendrical.Islamic.UmmAlQura.Astronomical— Astronomical implementation of the Umm al-Qura rule using theAstrolibrary's sunset/moonset and lunar phase functions for Mecca. Available for research and validation against the embedded table.Calendrical.Islamic.ObservationalandCalendrical.Islamic.Rgsa— observational Islamic calendars using actual crescent visibility computed byAstro.new_visible_crescent/3(Odeh 2006 criterion). The two share a privateCalendrical.Islamic.Visibilityhelper and differ only in observation location (Cairo vs Mecca al-Masjid al-Ḥarām).Calendrical.Hebrew— arithmetic Hebrew calendar with the molad of Tishri and Lo ADU Rosh postponement rules. Public API uses CLDR's Tishri = 1 month numbering with month 6 (Adar I) only valid in leap years. Overridesmonth_of_year/3to return{7, :leap}for Adar II so localization picks up the CLDR7_yeartype_leapvariant.Calendrical.Chinese,Calendrical.Korean(Dangi), andCalendrical.LunarJapanese— lunisolar calendars sharing aCalendrical.Lunisolarbase implementation. UseAstrofor lunar phase and winter solstice calculations at Beijing/Seoul/Tokyo respectively.
Calendrical.Composite— adefmacro __using__template for building composite calendars that use one base calendar before a specified date and a different calendar after. Supports any number of transitions chained together. The pre-builtCalendrical.EnglandandCalendrical.Russiamodules demonstrate the historical Julian-to-Gregorian transitions.Calendrical.Era— an@after_compilehook that auto-generates aCalendrical.Era.<CalendarType>module from CLDR era data. Calendarsuse Calendrical.Behaviourget era support for free without writing any era boundary code. ETS-based locking coordinates module creation for calendars that share acldr_calendar_type.Calendrical.localize/3— locale-aware names for:era,:quarter,:month,:day_of_week,:days_of_week,:am_pm, and:day_periodsparts of any date. Falls through to all 766+ CLDR locales available fromLocalize.Calendar. Handles the CLDR_yeartype_leapvariant for Hebrew Adar II without needingmonth_patternssubstitution.Calendrical.strftime_options!/1— returns a keyword list compatible withCalendar.strftime/3so the standard library's formatter can produce locale-aware output for any Calendrical calendar.Calendrical.shift_date/5andCalendrical.shift_naive_datetime/9— calendar-aware date/datetime shifting that supports the standardDate.shift/2andNaiveDateTime.shift/2APIs across every Calendrical calendar.Calendrical.Interval—Date.Rangefor years, quarters, months, weeks, and days in any supported calendar. TheCalendrical.Interval.relation/2function implements Allen's interval algebra (precedes, meets, overlaps, contains, …).Calendrical.Kday— finds the n-th occurrence of a given weekday relative to a date (e.g. "the second Tuesday in November", "the last Sunday before Christmas").Calendrical.FiscalYear— pre-built fiscal calendars for 50+ territories (US, AU, UK, JP, …). TheCalendrical.FiscalYear.calendar_for/1factory creates a fiscal calendar for any supported ISO 3166 territory code.Calendrical.FormatandCalendrical.Formatter— calendar formatting via a behaviour-based plugin system. IncludesCalendrical.Formatter.HTML.Basic,Calendrical.Formatter.HTML.Week, andCalendrical.Formatter.Markdownfor rendering calendars to HTML and Markdown. Custom formatters can be added by implementing theCalendrical.Formatterbehaviour.Calendrical.Parse— parses ISO-8601 date and datetime strings into the calling calendar viaparse_date/1,parse_naive_datetime/1, andparse_utc_datetime/1.Calendrical.Preference—calendar_from_locale/1andcalendar_from_territory/1return the preferred calendar for a CLDR locale or ISO 3166 territory.Calendrical.Ecclesiastical— Reingold-style algorithms for the dates of Christian liturgical events in a given Gregorian year, organized into three traditions:Western (Roman Catholic / Anglican / most Protestants, Gregorian computus, results returned as
Calendrical.Gregoriandates):easter_sunday/1,good_friday/1(two days before),pentecost/1(49 days after),advent/1(the Sunday closest to 30 November),christmas/1(25 December),epiphany/1(first Sunday after 1 January, US observance).Eastern Orthodox (Julian computus, results returned as
Calendrical.Juliandates so the calendar context is visible):orthodox_easter_sunday/1,orthodox_good_friday/1(two days before),orthodox_pentecost/1(49 days after),orthodox_advent/1(the start of the Nativity Fast on 15 November Julian — Eastern Orthodoxy has no movable "Advent Sunday" equivalent),eastern_orthodox_christmas/1(25 December Julian, projected onto the Gregorian calendar).Astronomical (the World Council of Churches' 1997 Aleppo proposal for unifying Western and Eastern Easter; not currently used by any Church, included for comparison; year range restricted to 1000..3000):
astronomical_easter_sunday/1(first Sunday strictly after the astronomical Paschal Full Moon),astronomical_good_friday/1(two days before),paschal_full_moon/1(the astronomical PFM itself, computed viaAstro.equinox/2andAstro.date_time_lunar_phase_at_or_after/2).
Plus
coptic_christmas/1(29 Koiak Coptic) which doesn't fit cleanly into any of the three traditions.The module's moduledoc includes a comparison table showing the three Easter computations side-by-side.
Eleven exception modules in
lib/calendrical/exception/, one per file, modeled after the Localize convention. Each has semantic struct fields, anexception/1constructor that takes a keyword list, and amessage/1callback that usesGettext.dpgettext/5for translation:Calendrical.IncompatibleCalendarError— fields:from,:to.Calendrical.IncompatibleTimeZoneError— fields:from,:to.Calendrical.InvalidCalendarModuleError— field:module.Calendrical.InvalidDateOrderError— fields:from,:to.Calendrical.MissingFieldsError— fields:function,:fields.Calendrical.InvalidPartError— fields:part,:valid_parts.Calendrical.InvalidTypeError— fields:type,:valid_types.Calendrical.InvalidFormatError— fields:format,:valid_formats.Calendrical.IslamicYearOutOfRangeError— fields:year,:min_year,:max_year.Calendrical.Formatter.UnknownFormatterError— field:formatter.Calendrical.Formatter.InvalidDateError— field:date.Calendrical.Formatter.InvalidOptionError— fields:option,:value.
Calendrical.Gettext— gettext backend for the Calendrical library, using the"calendrical"domain with four contexts:"calendar","date","format", and"option".Embedded CLDR Umm al-Qura reference data sourced from R.H. van Gent's Utrecht University dataset (1356–1500 AH), cross-referenced against the KACST published tables. The data is encoded as compile-time module attributes and consumed via O(1) and O(log n) lookup.
Changed (vs. ex_cldr_calendars)
All
Cldr.Calendar.*module names renamed toCalendrical.*. The detailed renaming map is inguides/migration.md.The
:cldr_backendoption and the entire backend-module architecture have been removed. Calendrical reads CLDR data directly fromLocalize.Calendarat runtime; no compile-time backend module is required. Functions that previously took a:backendparameter no longer accept one.Error returns use the modern Elixir convention
{:error, %Exception{}}instead of the legacy two-tuple form{:error, {ExceptionModule, "message"}}. Callers can pattern-match on the exception's structured data fields (e.g.%Calendrical.MissingFieldsError{function: f, fields: fs}).Exception names ending in non-
Errorsuffixes have been renamed to use theErrorsuffix consistently with Localize (Calendrical.MissingFields→Calendrical.MissingFieldsError,Calendrical.InvalidCalendarModule→Calendrical.InvalidCalendarModuleError, etc.).Calendrical.Hebrewnow uses CLDR's Tishri = 1 month numbering instead of Reingold's Nisan = 1 numbering. The previous numbering produced wrong localized month names because CLDR Hebrew data uses Tishri = 1.Calendrical.shift_date/5andCalendrical.shift_naive_datetime/9now apply duration units in the standard order (years → months → weeks → days), matching the Elixir stdlibDate.shift/2convention. The oldCldr.Calendar.plus(date, %Duration{})applied units in the opposite order.Calendrical.Durationhas been removed. Use Elixir's built-in%Duration{}struct (since Elixir 1.17) andDate.diff/2instead.The
plus/minuscallbacks have been removed from theCalendricalbehaviour. Calendar arithmetic is now driven exclusively byDate.shift/2/NaiveDateTime.shift/2, which delegate to the calendar'sshift_date/4,shift_time/5, andshift_naive_datetime/8callbacks.All conditional code that supported Elixir versions older than 1.17 has been removed. Calendrical now requires Elixir 1.17+ and Erlang/OTP 26+, matching Localize. Removed 24 obsolete
Code.ensure_loaded?/function_exported?/Version.match?guards across 7 files.Calendrical.paschal_full_moon/1has moved toCalendrical.Ecclesiastical.paschal_full_moon/1. The new home is alongside the rest of the Christian-calendar functions.
Removed
Cldr.Calendar.Duration— replaced by Elixir's built-in%Duration{}.The
MyApp.Cldr.Calendar.*backend modules and thecldr_backend_provider/1callback. All locale data is now read fromLocalizeat runtime.Calendrical.plus/{4,5,6},Calendrical.minus/{4,5,6}, theplus/6callback inCalendrical.Behaviour, and the corresponding:monthsclause inCalendrical.Base.MonthandCalendrical.Base.Week. UseDate.shift/2/NaiveDateTime.shift/2instead.Calendrical.Sigils(the~dsigil). Elixir's native~Dsigil has supported a trailing calendar suffix since Elixir 1.10 and works for any module implementing theCalendarbehaviour. Use~D[2024-09-01 Calendrical.Hebrew]instead of~d[2024-09-01 Hebrew]. TheCalendrical.Sigilssigil's other features (default ofCalendrical.Gregorian, ISO week-date formatyyyy-Wmm-dd, fiscal calendar shortcuts, B.C.E./C.E. era markers) are minor conveniences that did not justify maintaining a parallel sigil system. Seeguides/migration.mdfor one-line equivalents of every removed feature.
Calendars
This release introduces 17 calendar implementations covering every CLDR-acceptable calendar type. See guides/calendar_summary.md for the full list grouped by family, with month structures, leap-year rules, and reference dates.