Calendars & Calendar Conversions

Tags: #<Tag:0x00007f114a0de078> #<Tag:0x00007f114a0ddb50> #<Tag:0x00007f114a0dd970>


@josevalim, @qqwy, sorry for being a bit quiet last 24 hours, a few other things got in the way but I’ve been following and on board with the discussion. Will be done in a couple of days after I update some tests around the leap second boundaries.

Yes, my current version is {days_since_epoch, float_fraction_of_day} as a tuple, not struct, in order to keep it simple and follow José’s advice.

I will have an updated PR for review in a couple of days that lev


Beautiful, thank you and please take your time. :slight_smile:


@kip wonderful! I look forward to your Pull Request :smiley: . And yes, take your time. It is not like the calendars are going anywhere in the meantime ;-).

(Humans truly are peculiar creatures, being able to discuss for hours about something that only takes a second :grin:)


2 posts were merged into an existing topic: Ex_calendar



Btw, shall we spawn a new thread for discussing ex_calendar? I would like to keep this one focused on the improvements to Elixir calendar itself. :slight_smile:


agree, ping @AstonJ to split threads


@Eiji @josevalim I have split the posts that are relevant to ex_calendar to its own topic. :slight_smile:


Yes and no. As we count in days and ‘day fractions’ (my previous post was an explanation of how such a day fraction could be stored in an integer format that would be precise enough to handle leap second conversions), to calculate the difference between two dates (in days + day fractions) will not need to handle leap seconds.

A date in Elixir (%Date{}) does not have fractions, so how would you be able to use fractions in the calculation?

but note the Calendar functions should not receive structs. Otherwise we have cyclic dependencies between those which, although they are fine in Elixir, it is usually not a good practice.

How about receiving maps that would be valid structs (e.g. Date, NaiveDateTime or DateTime )?

Then all alternative calendars could have functions for to_iso and from_iso for each type Date, NaiveDateTime and DateTime that would receive/return these maps. That would make comparisons simple. You just convert to ISO and compare.


In cases of a %Date{}, we count in complete days. In cases of a %DateTime{}, we count in fractions. The problem when converting a date in one calendar to another, is that you do not know when on the day it is, and as the roll-over point between days varies between calendars (common ones are sunrise, noon, sunset and midnight), this extra information is needed if you want to unambiguously convert. In the cases of converting a date (and not a datetime) there might be more than one possible result day.

Yes. The problem with to_iso and from_iso, is that all other calendars will need to work with the complexities of the current ISO 8601 calendar, which should not at all have to be necessary if you want to compare between two non-ISO8061 calendar moments. As soon as you not only work with dates (side note: you already run into a problem there, as one thing that is unspecified in ISO8601 is what happens with dates before 1582), is that all other calendars need to handle the problem of the leap second. (And as mentioned before, ambiguity arises when only handling dates.)

Exactly this, ‘not needing to work with leap seconds’, as well as the rigorous effort that Dershowitz and Rheingold have made in their work to specify existing calendars using the Julian Day system was the reason to create the new internal format.

Besides, if you’d write your own conversion from another (non-trivial) calendar to ISO8601, it is very (arguably, the most) natural to first convert this date to a number of days and then convert this to ISO8601-format.
Thus, if we’d convert between Calendar A and Calendar B, and the only thing we have is from_iso and to_iso, this is what would happen:

date in calendar a
|> number of days.
|> to_iso
|> from_iso
|> number of days
|> date in calendar b.

Clearly there is duplicated effort (both in computational complexity and in programming complexity) here.

I think this is the correct approach :slight_smile:!


For dates before 1582, that is specified here: Which follows Erlang’s approach. So that is one less concern.

Do we want alternative calendars to be able to convert to Calendar.ISO? If so, the libraries have to concern themselves with the Gregorian rules. I don’t see how you can get around that by introducing a different intermediate format.


We want Calendar.ISO to be convertable to and from a number in days, and Calendar.Julian, Calendar.HybridGregorian, Calendar.Hebrew, Calendar.Chinese, Calendar.ISOWeekDate etc. to be convertable to a number in days. Now these other calendar implementations do not need to know about Calendar.ISO’s design choices and inner workings at all.


Hello everyone :slight_smile:

Can we have an update about the progress of this issue?


To whom it may concern: I’ve started a new Pull Request that implements the conclusions of our discussion so far.


And after a lot of discussions, changes, more discussions and more changes, the Pull Request (which became quite the giantess) has been merged!

There will be some more clean-up happening after, most notably Calendar still depends on :calendar which means dat date/times before the ISO8601 ‘year 0’ are not supported, which could be changed to a custom implementation.

But the main work is done. We can start building libraries that implement other calendars, and convert them to and from each of the different formats now!

:sunglasses: I am very happy.


That was’t an easy task.
Nice job. Thank you very much :heart:

I think after this elixir has one of richest and strongest calendar systems :sunglasses: