Qqwy
Calendars & Calendar Conversions
As the talk about calendars, differences between calendars and the difficulties of converting dates and times from one calendar to the other got a little bit out of hand in the topic of the Jalaali calendar library, this discussion can continue here.
The problems with calendars
(a short summary of the discussion so far)
- Calendars are often irregular in an unpredictable way: Leap days, leap seconds, next month only starting if the moon is visible, etc.
- Different calendars use different measures (such as different times at which one day ends and the next one starts: midnight, noon, sunrise, sunset?)
- The OS clock in our computers ‘counts seconds’ but does not handle leap seconds, which complicate the conversion between OS (POSIX) time and datetimes in other calendar systems.
Now, of course: How would it be possible to overcome these problems as good as possible?
- We can look at already-existing calendar implemetations such as Joda Time.
- As @kip noted, Dershowitz and Rheingold described conversions between 23 calendars by using a fixed date representation. (He currently has an open pull request to add an integer calendar representation to the core)
- This representation might be enhanced by choosing a smaller denomination than integer ‘days’ to make the results truly unambiguous and allow conversions between calendars of different times.
- One such intermediate representation might be TAI (International Atomic Time), as it is one of the few calendars that is monotonically increasing and computationally simple.
- However, converting OS time to TAI again requires knowledge of leap seconds, which means that results will be unaccurate starting +6 months from now.
- Also, precision will diverge when we go too far in the past or future in any case, due to small irregularities or inaccuracies between calendars.
Calendars are hard (but interesting!). Let the discussion on how an accurate calendarium (for Elixir?) might be built continue!
Most Liked
kip
@Qqwy, thanks for starting this thread. And @alisinabh, apologies for hijacking your thread. From the earlier conversations and @qqwy’s summary above, i believe consensus so far is:
- A calendar conversion mechanism in the
Calendarbehaviour is a good idea - That the conversation mechanism should cater for dates and times (ie. conversion of
Date,NaiveDateTimeandDateTime - Desirably the mechanism preserves monotonicity of time
The candidates I have seen in discussion so far are:
Julian Date (JD)
This is well understood, especially in astronomy circles. In incorporates an integral number of days since an epoch (epoch depends on which version of JD is chosen, but the original epoch of Julian day number 0 is assigned to the day starting at noon on January 1, 4713 BC, proleptic Julian calendar (November 24, 4714 BC, in the proleptic Gregorian calendar). The fractional part represents the fraction of a day since noon.
Plus: well understood, incorporates date and time, conversions have good precision
Minus: float format means time conversions are approximate and therefore whilst quite precise, cannot be assumed to be accurate in the way formal time systems are accurate.
Rata Die
This is the mechanism used by Derschowitz and Rheingold. This is essentially the same mechanism as Julian Day but with epoch of 1 January 0001. In their book Calendrical Calculations, RD is sometimes used as an integer meaning number of days since epoch - and this is what I originally proposed in order to avoid the issues of time conversion. An alternative form uses the same approach as JD, using a fractional part of a float to denote fraction of a day since noon.
Plus: Mostly the same as JD. According to Wikipedia this is used by Go, Rexx and .Net. Another benefit is that D&R have described and tested the algorithms for 23 calendar types.
Minus: Same as JD, and also it seems to be less familiar than JD
Unix timestamp
Common timestamp format so well understood. As long as its 64 bits (as Elixir is) then precision is enough to cater for all practical date/time ranges
Plus: well understood epoch of January 1, 1970 Gregorian; many other library functions that can work directly on Unix timestamps. Precise in that only integer arithmetic is required whereas JD and Rata Die require float arithmetic.
Minus: Not monotonic due to leap second issues
TAI (Temps Atomique International)
Monotonic clock which underpins UT1 time and is part of the UTC time standard means the format is standardised (several formats are available from accuracy of seconds down to nanoseconds depending on needs).
Plus: Monotonic
Minus: Difficult to compute, requires calculation of drift from UTC and a leap second table. Must adjust each time a leap second is declared.
This is clearly not a definitive list, nor a complete explanation. Given that the primary discussion here is about calendar conversion, I would propose JD as the basis of conversion since that is familiar to calendarises, allows calculation of both arithmetic and astronomical calendar conversions and is computationally quite easy to calculate. Clearly I’m also ok with Rata Die (using the float form to represent time) since there is a solid body of work to be leveraged but that is still true using JD. I think despite the benefits, TAI is too complicated to calculate and to maintain.
Qqwy
I think we could, as long as we define a very clear translation of day fraction ↔ microseconds. This might be as simple as saying ‘there are always 86_400_000_000 microseconds in a JD (or RD)’. (86_400_000_000 == 24 * 60 * 60 * 1_000_000)
This does mean that extra care needs to be taken when converting to/from UTC datetimes because of the afore-discussed leap second troubles:
On days at which UTC has a leap second, the conversion of an UTC microsecond is not 1:1 to a JD/RD microsecond, but rather 86401 : 86400 (an UTC day with a leap second contains 24 * 60 * 60 + 1 seconds). Most timestamps with microsecond integer precision will have to be rounded when doing this conversion.
As the gcd of 86400 and 86401 is 1, if we want to use an integer base in which no rounding would occur, we need to multiply it with the common base (86400 * 86401) == 7465046400 first.
So, if we want to have an exact (monotonically increasing) result, we’d need to store microseconds * 7465046400 in the second tuple field.
This does sound like a bit of hard work, but the nice thing about using a {day, in_day_amount} tuple is that the answers will always be exact and fast to convert to- and from UTC on any non-leap second days (Using for instance TAI as intermediate standard we would not be able to do this as in that case, every datetimestamp past a leap second needs to keep track of that leap second).
I believe we’d be able to claim microsecond exactness for all times on days except the June 30ths and December 31ths that are more than 6 months in the future; during those days, this calculation might at most be (amount_of_years_more_than_six_months_in_the_future * 2 * 86401) / 86400 seconds off.
(note: it theorerically is also possible that there are leap second deletions, in which case an UTC day might only have 86399 seconds. This has never happened during the past 45+ years that leap seconds are part of the internationally used calendar, but if we’d want to support it, we’d need 86399 * 86400 * 86401 as base.)
josevalim
Date/DateTime should not be a global structure, every calendar could potentially represent Date/DateTime differently and they should be calendar specific. You can use protocols or type_class’s or so to get generic information out of ‘any’ calendar of course.
There is absolutely nothing stopping this from happening today. Otherwise it is like saying you cannot implement another key-value data structure because Elixir already has a Map module.
If you want key-value genericity, you can define the protocol and you can define new data types. The same for the calendars data structures.
Given the Gregorian Calendar is the international de facto and using it as a base is what makes sense for the huge majority of developers and the huge majority of times, it makes sense to prioritize its representation.
So while it is very interesting to see how far we can take the calendar structures we have today, I have absolutely no problem with saying we won’t support calendars X, Y and Z since the problem can be solved by a third party. In fact, we may even arrive to the conclusion that the :calendar field is unnecessary and any other calendar should be integrated via a protocol. All it takes is someone to define the protocols in a separate library, call it Zoolendar or whatever, which everyone can depend on when building new implementations.
Popular in Discussions
Other popular topics
Categories:
Sub Categories:
Forums
Popular Tags
- #ecto
- #liveview
- #troubleshooting
- #learning-elixir
- #deployment
- #library
- #erlang
- #testing
- #genserver
- #mix
- #absinthe
- #remote-other
- #otp
- #plug
- #how-to-question
- #macros
- #postgres
- #channels
- #elixirconf
- #exunit
- #discussion
- #code-sync
- #javascript
- #podcasts
- #onsite
- #dialyzer
- #docker
- #authentication
- #umbrella
- #full-time-contract
- #podcasts-by-brainlid
- #ecto-query
- #elixir-ls
- #phoenix_html
- #iex
- #blog-post
- #graphql
- #genstage
- #ai
- #websockets
- #supervisor
- #advent-of-code
- #elixirconf-us
- #distillery
- #processes
- #forms
- #api
- #metaprogramming
- #security
- #performance








