Notice by @qqwy: This topic was split from the Calendars & Calendar Conversions topic, which is about how calendars should be implemented inside Elixir Core. The idea of a separate ex_calendar library started with this post by @OvermindDL1:

Just a brain dump here since I am sick. ^.^

Not all Calendars use a year/month/day/hour/minute/second/etc… construct, so putting that as a base-most Date/DateTime structs was a very very bad move. Each Calendar should have its own Date/DateTime structs, which also allows easy matching on:

def do_blah(%Calendar.Gregorian{year: year, month: month, day: day}), do: gregorianStuff(year, month, day)

def do_blah(%Calendar.Bloop{egrek: egrek, vreenoop: vreenoop}), do: bloopStuff(egrek, vreenoop)

# Using expede's type_class (finally hit 1.0.0 whoo!) style here to force a set of generic conversions for calendar types
def do_blah(someOtherCalendar), do: Calendar.convertToCalendar(Calendar.Gregorian) |> do_blah

The above last line should use a direct conversion if one exists from calendar type to the given calendar type, else it should fall back to a mandatory conversion to some universal time storage type that could be something like: %UniversalTime{attosecondsSinceBaseTimeInObject:int, baseObject:atom|binary} where attosecondsSinceBaseTimeInObject would be the number of attoseconds since some arbitrary base period for the given baseObject, which could be its existence setup or something, and baseObject is the relative object that the relative time is for, which would default to :earth or so. Converting a UniversalTime for an object like :mars to some calendar like Calendar.Gregorian makes no sense and should exception or something, instead you’d need to calculate the relative earth time from mars first, also handled by the type_class/protocol by getting the relative calculations from the objects based on velocity and such, but for something like :earth/:mars you could pretend that is just 0 probably, this is all internal stuff that an external user would not use directly in most cases of course, though if you are making a relativistic simulation it could be quite useful externally, but mostly it is just for calendar conversions.

TL;DR: 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.

I’m going to lay back down for a bit. ^.^


@OvermindDL1: I just pushed initial commit for project that visualize our idea: ExCalendar.
I defined only two protocols and simple ExCalendar.Calendar module.
Of course I’m not expert of English and calendars, so it’s not final version. For example in protocol methods declarations I used Erlang date/time Tuple as base format.
What do you think about it?

Hmm, not a bad start, but using the erlang date/time tuple still assumes things about a calendar that is very likely not to exist in the future (we ‘are’ going to the moon and mars soon after all). This is why I still think that some amount of fixed time from a fixed point in spacetime is required as the base time unit (which for earth can just be seconds since the unix epoch, so unixtime, no leap second calculations or anything of the sort, just raw second or microseconds or attoseconds or whatever since the unix epoch). Calendar’s should be forced to convert to/from this relative spacetime value to their local representation (time is not a human construct, but dates are (based from a space and time)).

Or, could encode the location into the Calendar just by virtue of how it works, but again that means Date/DateTime/Time needs to be moved ‘into’ the calendar. ^.^

@OvermindDL1: yup, I know that Erlang date/time Tuples are bad in our situation. As I already wrote it’s only a demo (initail release) - I used it, because temporary something should be there to let others know how it will be looks like in future (same as word something in one method). How about start date/event to compare time presentations? Is attosecond approved? If not I will wait for approved base time presentation for Elixir and update my work. Do you have any other tips? Am I missed an important method or something else?

@OvermindDL1 I really like the idea of a fixed spacetime value of time. By chance, I happened to come across Barycentric Dynamical Time (TBD) on Wikipedia a few times ago, which is exactly that.
However, I do want to note that when you’re working with a representation like this, then what you’re doing is rocket science: Calculating to- and with this format is going to involve very difficult (relativistic) calculations.

Also, for most computers that do not directly keep time with an atomic clock, the input timestamp will be too unprecise to calculate results that would actually be useful for rocket science; Garbage In, Garbage Out.

For most projects, a time representation like this is a very clear case of YAGNI.

@Eiji Writing some code so you have something to talk about is good. However, keep in mind that it is very easy to let a discussion move towards bikeshedding when you start filling in details too quickly, before the problem in general is fully described/known. Furthermore, it is easy as a human to pick ‘a solution algorithm’ right away, before thinking about all subproblems of the main problem. This results in code that does not solve the main problem fully, which means that it will need to be rewritten later (and this might be a lot of work if you find out about this).
That being said: I love your enthusiasm! :slight_smile:
To address the questions you have about your current code:

  • Instead of using a date/time Tuple, I would suggest using a custom struct that is defined as part of ExCalendar. Any other modules that want to alter something of this struct should do so by calling methods on the Struct’s module (so not by changing the fields of the struct directly). This keeps you free to completely overhaul the internal representation of the struct later.
  • As for what kind of base time representation unit you’d want to use: @OvermindDL1 probably also has an opinion about this (and I believe he knows more about Physics than I do), but I think Planck time is your best bet (also see Orders of magnitude of time).

I think your library should not be concerned about creating data structures. The question you should answer is: assuming that many calendar structures exists, how can I work with them without caring about their representation? That’s when a protocol comes in.

For example, such function makes no sense in a protocol: https://gitlab.com/ex-open-source/ex_calendar/blob/master/lib/ex_calendar/calendar/time_presentation_protocol.ex#L43

Remember that the first argument of a protocol function is what we use for dispatching. In this function, the first argument will always be tuples, which means we will always dispatch to the tuple implementation and therefore you will never be able to create different data types.

I believe instead you should focus on the functions required for calendar interoperability. For example, imagine you want to calculate the difference between two dates in different calendars, how would you do that? You could for example add diff to the protocol… but wouldn’t it be more productive to simply have a common format both dates would be converted to and then you operate on that format?

Similarly, how do you add days to a given date in any calendar? Again, you could define an add function. But, if possible, wouldn’t be better to once again convert to a common format then perform the operation?

A good protocols finds the smallest API surface require to provide its functionality (which is one of the main reason protocols do not allow default implementations).


Right, it’s why pushed only initial work - for example without even simple “blank” implementation of calendar. It’s a skeleton that needs to be modified. We started from idea. Now lets make it usable!


Planck time looks really good, but it’s ~5.39116(13)*10^-44s, so we need to round it, right? Is it a good to use rounding on so small unit? How it could affect on much more higher like days, weeks, years?

True, it’s my bad.

I have an idea for new version. I will push it tomorrow. Thanks for your responses!

@josevalim @Qqwy @OvermindDL1: just pushed new version of ex_calendar.
I added unit behavior, so any developer may easy work only on units. I changed definition of time presentation from protocol to behavior, because I think it will work better here. Calendar is still protocol, so we can list all it’s implementations. Calendar (as in previous version) have method to list supported time presentations and finally time representation have callback to list all supported units. Both time presentation and unit have callbacks to convert from and to universal unit. I added also some method ended with ! character, so we can check or just raise if passed unit name (like :hour) is supported or not. I also updated description and added one more diff method that returns value in universal unit. At end I added translate_units method, so developers do not need to care if they should add 0.5 minute or 30 seconds (I mean: what to use? Float or Integer or any of them? - no matter just translate them).
I hope that I don’t forgot about anything. Maybe some names are still bad (sorry for my English), but I believe that new version of skeleton is now much more better and closer to resolve main problem.

1 Like