{"id":18924740,"url":"https://github.com/rengel-de/calendars","last_synced_at":"2026-03-14T05:30:19.889Z","repository":{"id":57481902,"uuid":"282207368","full_name":"rengel-de/calendars","owner":"rengel-de","description":"Calendar Collection based on Calixir","archived":false,"fork":false,"pushed_at":"2020-11-03T15:10:06.000Z","size":120,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"master","last_synced_at":"2025-01-31T20:09:02.774Z","etag":null,"topics":["calendars","converters","elixir"],"latest_commit_sha":null,"homepage":"","language":"Elixir","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/rengel-de.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2020-07-24T11:53:48.000Z","updated_at":"2020-11-03T15:10:09.000Z","dependencies_parsed_at":"2022-09-02T04:11:43.509Z","dependency_job_id":null,"html_url":"https://github.com/rengel-de/calendars","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rengel-de%2Fcalendars","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rengel-de%2Fcalendars/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rengel-de%2Fcalendars/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rengel-de%2Fcalendars/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/rengel-de","download_url":"https://codeload.github.com/rengel-de/calendars/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":239921875,"owners_count":19718842,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["calendars","converters","elixir"],"created_at":"2024-11-08T11:07:55.427Z","updated_at":"2026-03-14T05:30:19.857Z","avatar_url":"https://github.com/rengel-de.png","language":"Elixir","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Calendars\n\n`Calendars` is a collection of calendars that is based on the calendars contained \nin the 4th edition of the book (from here on referenced as _DR4_)\n     \n[Calendrical Calculations - The Ultimate Edition](https://www.cs.tau.ac.il/~nachum/calendar-book/fourth-edition/)  \nby Edward M. Reingold and Nachum Dershowitz  \nCambridge University Press, 2018\n \nThe calendars are just thin wrappers around the [Calixir](https://hex.pm/packages/calixir) \npackage. `Calixir` is a port of the Lisp calendar software `calendrica-4.0.cl` that comes\nwith DR4.\n\n## Installation\n\nThe package is [available in Hex](https://packages/calendars) and can be installed\nby adding `calendars` to your list of dependencies in `mix.exs`:\n\n```elixir\ndef deps do\n  [\n    {:calendars, \"~\u003e 0.1.0\"}\n  ]\nend\n```\n\n## Documentation\n\nDocumentation has been generated with [ExDoc](https://github.com/elixir-lang/ex_doc)\nand published on [HexDocs](https://hexdocs.pm). The docs can be found \nat [https://hexdocs.pm/calendars](https://hexdocs.pm/calendars).\n\n## Copyright and License\n\nThe Calixir library and its sample data are made public under the following conditions:\n\n- The code and data can be used for personal use.\n- The code can data be used for demonstrations purposes.\n- Non-profit reuse with attribution is fine.\n- Commercial use of the algorithms should be licensed and are not allowed from this library.\n\nThe permissions above are granted **as long as attribution is given to the authors of the \noriginal algorithms, Nachum Dershowitz and Edward M. Reingold**.\n\n## About the Calendars\n\nIn this software, I distinguish __monotonic__ and __cyclical__ \ncalendars:\n\nA __monotonic__ calendar has a distinct origin or _epoch_ and \nstretches from there (potentially) to negative and positive infinity. \nAll its values or _dates_ are distinct. The most common example is \nprobably the `Gregorian` calendar.\n\nA __cyclical__ calendar does not have an origin. It consists of periods or \n_cycles_ of the same length that return indefinitely. All the values or \ndates within one cycle are unique, but they return from cycle to cycle, \nso that it is not possible, to assign such a value to a distinct point \nin time. The most common example is probably the week with its weekdays. \n\nOne could argue that cycles shouldn't be called 'calendars'. But that's the \nway they are treated in DR4. So I'm using the attributes _monotonic_ and \n_cyclical_ to distinguish calendars if and when it matters.\n\n## About the Calendar Conversions\n\nTo facilitate the conversion of calendar dates between different calendars \nis one of the main functions of `Calendars`. To minimize the number of conversion  \nfunctions to be written the common star pattern is used. Each conversion from \none calendar to another is split in two steps:\n\n1. Conversion of the date of the source calendar into a common 'canonical' date.\n2. Conversion of the 'canonical' date into a date of the target calendar.\n\nThe 'canonical' date is the central hub for all conversions.\nThus, every calendar must only know how to convert its dates into and from \nthe corresponding date of some 'canonical' calendar. Prime candidates for a \n'canonical' calendar are calendars that don't have an internal structure of \ntheir own (i.e. a hierarchy of units like 'year', 'month', and 'day'), but \nwork with one unit only (usually the 'day').   \n  \nOne candidate could have been the Julian Day number (`JD` in this package) or\nvariants thereof (i.e. the Modified Julian Day number, `MJD`). But the \nauthors of DR4 have chosen to create their own 'canonical' calendar,\nthe `RataDie` calendar, that is closely aligned with the `Gregorian` calendar.\n\nThe base unit of the `RataDie` is the _day_. Throughout the book (DR4) a  \nvariable containing a day of this calendar is referred by the term _date_ \nwhile functions working with such a variable use the term _fixed_, i.e.:\n\n```\ngregorian-from-fixed(date) = [year, month, day]  # DR4 62 (2.23)\n``` \n\nI consider this unfortunate, because _date_ is a broader term that can be \n(and commonly is) used for all calendars. So throughout `Calixir` and `Calendars`, \nI use the term _fixed_ instead of _date_ for dates of the `RataDie` calendar, i.e.:\n\n```elixir\ngregorian_from_fixed(fixed) = {year, month, day}  # DR4 62 (2.23)\n``` \n    \nThe `fixed` dates of the `RataDie` calendar form the basis for all calendar \nconversions in `Calixir` and `Calendars`. \n- Both, **monotonic** and **cyclical** calendars must have a `from_fixed` function\nthat converts a `fixed` date into their own corresponding dates. \n\n- In addition, **monotonic** calendars must have a `to_fixed` function\nthat converts their own dates into the corresponding `fixed` dates. \n\nAdditionally, the calendars have two pairs of conversion functions that are just\nsyntactic sugar:\n\n- `from_jd` and `to_jd` for Julian Day numbers\n- `from_date` and `to_date` for direct conversions from and into another calendar.\n\n## The WHYs and the WHY-NOTs\n\nIn this section, I detail some of my decisions why this software is how it is. \nIt might help you to understand its structure. \n\nBefore I published the `Calixir` and `Calendars` packages I tried \nvarious ways to refactor the monolithic `calendrica-4.0` Lisp package, but always \nended in some form of 'dependency hell'. So I finally decided to keep `Calixir` \nas a single monolithic block and implement the calendars as thin wrappers around \n`Calixir`.\n\nThis approach offers an additional advantage: The `Calendars` collection can be \nextended just by adding calendar modules to `lib/calendars`. Additional calendars \ncan but don't need to reference `Calixir`. To allow for conversions they only \nhave to implement their own `from_fixed`, `to_fixed`, `from_date`, and `to_date` \nfunctions. `from_jd` and `to_jd` are not required.\n\nIf you need additional functionality for a calendar create a new module and use  \nexisting functionality by creating `defdelegate`s to the base calendar or `Calixir`.   \n\n## Usage\n\nHere is an example for the interactive use of `Calendars`:\n\n```\nD:\\Projects\\calendars\u003eiex -S mix\nInteractive Elixir (1.9.4) - press Ctrl+C to exit (type h() ENTER for help)\niex(1)\u003e g_date = Gregorian.date(2020, 7, 24)\n{2020, 7, 24}\niex(2)\u003e fixed = Gregorian.to_fixed(g_date)\n737630\niex(3)\u003e j_date = Julian.from_fixed(fixed)\n{2020, 7, 11}\niex(4)\u003e Julian.to_date(j_date, Gregorian)\n{2020, 7, 24}\n```\n   \nThe same example in a module:\n\n```elixir\ndefmodule ExampleConverter do\n\n  def gregorian_to_julian_conventional do\n    g_date = Gregorian.date(2020, 7, 24)  # create Gregorian date \n    fixed = Gregorian.to_fixed(g_date)    # convert Gregorian date to fixed\n    j_date = Julian.from_fixed(fixed)     # convert fixed into Julian date\n    Julian.to_date(j_date, Gregorian)     # should return {2020, 7, 24}\n  end\nend\n```\nUsing a pipeline:\n\n```elixir\ndefmodule PipelineConverter do\n\n  def check_pipeline do\n    gregorian_date = Gregorian.date(2020, 7, 24)\n\n    if (gregorian_date\n        |\u003e Gregorian.to_fixed\n        |\u003e Julian.from_fixed\n        |\u003e Julian.to_fixed\n        |\u003e Gregorian.from_fixed) == gregorian_date,\n    do:   {:ok, \"Pipeline works.\"},\n    else: {:error, \"Pipeline is broken.\"}   \n  end\n\n  def check_pipeline_using_from_date do\n    gregorian_date = Gregorian.date(2020, 7, 28)\n    \n    if (gregorian_date\n        |\u003e Julian.from_date(Gregorian)\n        |\u003e Hebrew.from_date(Julian)\n        |\u003e Gregorian.from_Hebrew()) == gregorian_date,\n    do:   {:ok, \"Pipeline works.\"},\n    else: {:error, \"Pipeline is broken.\"}   \n  end\n\n  def check_pipeline_using_to_date do\n    gregorian_date = Gregorian.date(2020, 7, 28)\n    \n    if (gregorian_date\n        |\u003e Gregorian.to_date(Julian)\n        |\u003e Julian.to_date(Hebrew)\n        |\u003e Hebrew.to_date(Gregorian)) == gregorian_date,\n    do:   {:ok, \"Pipeline works.\"},\n    else: {:error, \"Pipeline is broken.\"}   \n  end\n\nend\n``` \n\nOf course, this works for any combination of monotonic calendars.\n\n\n## On Testing\n\nBe careful when running the tests. The `brute_force_conversion_test`\nmight take about half an hour. All the other tests combined take only\na few minutes.\n\n\n## Changelog\n\n### 0.2.5\n- Some corrections in README.md\n\n### 0.2.4\n- Added Astronomical calendar\n\n### 0.2.2\n- Cleaned up tests\n\n### 0.2.1\n- Simplified `atomize` function\n\n### 0.2.0\n- Completely rewritten logic and documentation\n\n### 0.1.6\n- Added functions `date_text` to Gregorian, Julian, and Hebrew.\n\n### 0.1.5\n- Fixed and renamed `_as_date` functions.\n\n### 0.1.4\n- Added function `today` to Gregorian.\n- Added function `start_of_year` to Gregorian.\n- Added function `end_of_year` to Gregorian.\n- Added function `start_of_month` to Gregorian.\n- Added function `end_of_month` to Gregorian.\n- Added function `start_of_week` to Gregorian.\n- Added function `end_of_week` to Gregorian.\n- Added function `month_names_as_list` to Gregorian.\n- Added function `day_names_as_list` to Gregorian.\n- Added function `month_names_as_list` to Hebrew.\n- Added holiday functions to Gregorian.\n- Added holiday functions to Hebrew.\n- Added the corresponding tests.\n\n### 0.1.3\n- Fixed docs in RataDie calendar.\n- Fixed `from_jd` and `to_jd` functions in RataDie calendar.\n- Added  `is_leap?` and `days_in_month` functions to Gregorian calendar. \n- Added  `is_leap?` and `days_in_month` functions to Julian calendar.\n \n\n\n  ","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frengel-de%2Fcalendars","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Frengel-de%2Fcalendars","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frengel-de%2Fcalendars/lists"}