New Date/Time Library: Interested?

Hi,

Some Date and Time features I needed for projects I was doing, was missing in the Date and/or Time libraries that were available at the time. I wrote my own library, mainly to be used in those project.

Now, I am starting to see it could become more, so I am thinking about publishing it and I would like some help with that. (Time for the hard part :wink: ) It is on github, btw: fmechant/chrono

In essence I am asking, do you think the library is interesting, helpful and ready to be published?
If not, what is needed?

How to Help

  • Feedback on the terminology:
    • Is it easy to understand, do you immediately understand what is meant?
  • Feedback on the API:
    • Can you easily find what you are looking for?
    • Is it easy to work with?
    • Is what you are trying to do easy?
    • Is it idiomatic to Elm?
  • Missing features:
    • Looking forward to a feature you are not finding?
  • Enough, clear documentation:
    • Is there enough documentation?
    • Is it clear enough?
    • Does it need more examples?
  • Correctness, testing:
    • Is there a case where you think the library is wrong?
    • Is it missing a test you would like to see?

Help with Missing Time Zones

Since we are using another implementation of time zones, we still need a library that
takes the tz database and makes time zones from that. Similar to justinmimbs/timezone-data.

Some help making that would nice :slight_smile: .

Some History

So, what was missing at the time?

Stuff like:

  • Convert a date to the next working day.
  • If you have a date and a time, tell me if it is now earlier or later.
  • If I get a Posix, is it between date/time this and date/time that (in a time zone, of course).
  • Convert date/time from and to posix (with time zone, of course).

So, I started writing a library myself to be able to easily do these things. Not on project time, but on my own free time. I wanted to tackle these problems myself, on my own pace.

Then, I found out how complex date/time really is, being confronted with daylight savings time problems, leap seconds and things like that. So, I started investigating and further adapted my library.
The focus was on how to make the library be correct, but still be easy to work with.

Then I realised that a time zone is actually just a collection of (bijective) mappings between date/time and moments in time. I started modeling my library like this, and all of a sudden, implementing the library became easy. Well, easier :wink: .

I started thinking it might be useful for everybody, so I added lots of documentation, fine-tuned here and there.

And now, I think I am ready to stick my head out and do the hard part. Please be nice :slight_smile:

fmech

1 Like

One thing Iā€™m missing from Date is function isDst : Zone -> Posix -> Bool which would tell whether given Time.Posix is within daylight saving time in given Time.Zone or not. This of course would require better internal timezone support which knows about DST.

I donā€™t know of any Elm package which does that. However there is justinmimbs/tzif which partially supports decoding real TZif (Time Zone Information Format) files, and Iā€™d love to see more complete version which parses more data from TZif-files, accompanied with a Date package which can make use of that additional data.

UPDATE: Actually what Iā€™d really want first is TimeZone package which fully exposes all/most data from TZif-files. Then Date build on that.

1 Like

Could you give me an idea why you need that kind of information? What would you do with it?

I am asking because date / time problems are complex and exposing this kind of information might trigger someone to use that to calculate things, but he may not be aware of all the complexities involved, and might introduce a bug because of that. (Thatā€™s what happened to me in the past :blush: )

To clarify, there is a function Date.toDateAndTime : TimeZone -> Moment -> DateAndTime that takes a Momentand calculates the Dateand Time in a specific TimeZone. It takes care of the DST conversions for you, under the hood. The TimeZone has to have this information, of course, that is why I ask help for creating TimeZones from the tz database.

I want to have full control for when I need/want it. For example:

  • When showing time to user, show also indicator of whether itā€™s within DST or not.
  • Compare these two timezones, listing all moments during next year when the relative offset between them changes.
  • Tell the next time DST changes in this timezone. And not just the exact moment when change happens, but the reasoning behind that moment, i.e. not just ā€œ2020-10-25 04:00ā€ but ā€œ04:00 on 2020-10-25 because it is last Sunday of Octoberā€.

I understand now. Your focus is on having everything there is about a time zone, so it is available when you need it.

The focus of chrono is working effectively with dates and time. A bit of a different point of view.

I am curiuos to see how other people feel about this.

Regarding wording, a time zone is not a time time offset. When you say customZone, how can I express that a specific year goes from to 30 december to 1 january, skining 31 december? Unifying time zones and time time zone offsets is a misleading simplifcation. Check this out:

For me the library is a bit confusing, because I donā€™t know what and how to transform julian days into dates I can understand, so I cannot verify if the examples make sense.

I like the idea of having a one in all solution for all that is date related, though. I will keep an eye on it :slight_smile:

3 Likes

I am not understanding everything you are saying, but let me try and answer some of your questions.

How to transform Julian days into dates I can understand:

import Chrono.GregorianCalendar as Cal

let
    someDate =
        Date.fromJDN 2440588
in
Cal.fromDate someDate
--> { year = 1970, month = Cal.January, day = 1 }

But maybe you mean manually? Then you can use https://www.aavso.org/jd-calculator.
But I get your feedback, maybe I should not use Julian Days in the examples, because that is actually an implementation detail.

How to define a TimeZone that skips 31 december:

If we look at customZone, we see we need to give it a Mappingand a list of Periods. What you are saying is, that at the moment that you would normally go to 31 December, you start a new period, with the datetime 1 January.
Suppose we want a time zone, that is like utc, but skips 31 December in 1999.
Your mapping is your ā€˜defaultā€™, where the epoch moment maps to 1 January 1970, 00:00:

mapping = 
    { moment = Moment.fromMsSinceEpoch 0
    , dateTime =
         { date = Cal.toDate { year = 1970, month = Cal.January, day = 1 }
         , time = midnight
         }
    }

Now you define a period that starts on 1 January 2000, but the moment is a day before it would happen in de default mapping:

period =
    { start =
        { moment = Date.toMoment utc { date = Cal.toDate { year = 1999, month = Cal.December, day = 31 }, time = midnight }
        , datetime = 
            { date = Cal.toDate { year = 2000, month = Cal.January, day = 1 }
            , time = midnight
            }
        }
    }

Which is, I think, readable. At the moment that in utc, it normally is 31 December 1999, 00:00, I want it to be 1 January 2000.

Finally create the time zone:

customZone mapping [ period ]

By the way, I am not sure why you would want to create a time zone like that.

Maybe I misunderstood completely? Let me know.

Perhaps he meant 30 December, as there exists a timezone which doesnā€™t have 30 December 2011:

At the end of Thursday, 29 December 2011, Samoa continued directly to Saturday, 31 December 2011, skipping the entire calendar day of Friday, 30 December 2011 and effectively re-drawing the International Date Line

From Wikipedia - Time in Samoa

Timezones are fun. :grin:

2 Likes

I changed the examples in the README to use Gregorian calendar days, and not Julian days.
Much more readable.

Thanks for the suggestion, @francescortiz.

Funny, @francescortiz, I am creating a blog about how date and time can be unexpectedly complex, and I added that very video at the end of the blog.

Publishing that blog soon ā€¦

Ah, yes, time zones. Funny things, they are.

There are time zones that have offsets like 15 minutes, time zones that not only depend on location, but also on Ethnic group, countries that change to DST only partly. I could you on, but will stop here.

Getting date and time right is hard. From my point of view, complete access to the browsers API would be sufficient for most purposes. Others may prefer a complete solution in ELM, but doubling a timezone-database in ELM would need much space, some 100 Kbyte

The Posix timestamp and optionally a timezone is enough to store time. Regarding to your example to get a date a week later at the same time and honoring time-zones: In JS you could use Date.Prototype.getDate(), add 7, and .setDate. You can ignore the overflow, but you have to stay within the local time-zone of the browser. So there could be a more complete solution, allowing such calculations in other time-zones, as your implementation.

In the meantime I use ports or webcomponentes to access the browsers API: https://github.com/hans-helmut/elm-timei18n Even in the case of removing DST as discussed in Europe, an update of the browser will be sufficient to fix it.

1 Like

Hey @fmech, thanks for contributing to the all things Time in Elm. Iā€™m keen to look over your library, as our app uses date and times extensively.

Had you see @justinmimbs other libraries time-extra and date?

PS - @justinmimbs, if youā€™re seeing this, I cannot express how grateful I am for all the work youā€™ve put into dates, times and timezones. Thank you so much. If you give me a mailing address, I will 100% send you some beer, cake, a bouquet of obscure tropical carnivorous plants, whatever youā€™re into.

2 Likes

Ooh, carnivorous plants! :yellow_heart: How did you know, @cmditch? Thank you for the kind words and the smile you put on my face.


In the top post, @fmech, you mention several motivating problems that werenā€™t straightforward to solve with existing libraries. From the descriptions, they each sound solvable with existing libraries, but the devil is in the details, and perhaps some of the solutions are not clear or ergonomic.

If you could give us concrete examples of your motivating use cases, then I think it would be helpful for either making improvements to existing libraries or making the case for a new library. :slight_smile:

3 Likes

Hi @justinmimbs, I refreshed my knowledge of the elm/time, justinmimbs/date, justinmimbs/time-extra packages, to be able to see what I was missing, back then.

I tried to remember and made one of the issues on Ellie. As you say, these things can be done with the current libraries. I am not sure any more if these were available back then, or what. I do not remember using justinmimbs/time-extra, so maybe that was what I was missing.

In any case, I want to avoid have multiple libraries doing the same thing. So maybe, I should just put it in the freezer?

Things that I see that are in chrono, and that could be added in the above libraries:

  • formatting time (12 hour format, AM/PM)
  • sorting dates

Going from Date.Date and time to Time.Posix is awkward because you have to go via Time.Extra.Parts, but that is because they are separate, independent libraries.

Summarizing some of the things that chrono does differently:

  • terminology: it tries to use the language we speak when people talk about dates and time (we never add, for example).
  • it separates the concept of Date and GregorianCalendar, enabling the ability to use other calendars.
  • it allows you to define how to handle impossible dates, when moving from January 31 one month into the future, for example (should it be February 28/29 or March 3/4 or ā€¦?)
  • it has a type to represent the time of day.
  • it is one library about date, time and moments.

What are you thoughts?

Hey, I donā€™t think this has already been mentioned here but I remember being pretty hyped by the post of @Herteby here a few months back about his work on type-safe dates and time: Typesafe unified Date and Time package using phantom record types. I havenā€™t used it personally though.

@mattpiz Btw, my package is far from done yet. There were a few things (mostly centered around those damn time zones) that I was unsure of how best to handle, and then I got distracted by my Lamdera game prototype :stuck_out_tongue: Since then Iā€™ve been feeling a bit unmotivated, but I will probably get around to it.

Thereā€™s a nicely curated catalog of Elm packages at https://korban.net/elm/catalog/, and if you select Date/time under Data, youā€™ll find 31 packages providing options for formatting, parsing, durations, calendars, etcetera. I donā€™t mean there isnā€™t room for more date/time packages, but exploring whatā€™s out there might help you decide where the gaps are and where to go from here.

For sorting, you can succinctly sort a list of Posix values with List.sortBy Time.posixToMillis, and you can sort a list of Date values with List.sortWith Date.compare or List.sortBy Date.toRataDie.

1 Like

Thanks, @justinmimbs, for the reference to the catalog.
Of those 31 packages, there are 7 that deal with the core representation of moments, dates or time.

All of those consider elm/time Posix for representing moments.
There are 3 packages that represent dates differently.
There are 3 packages that represent time (the thing you can check on your watch) differently.

If I where to add chrono to the mix, I feel I am hurting the Elm community more than helping it.

Isnā€™t there a way that we can make sure Elm has one Date, one Time, one Moment/Posix? I would probably invest some time in that.

Just to be clear, I donā€™t mind having lots of packages regarding date and time, but I feel having lots of core representations is not helpful.

Contact the lib authors specifically and see what they say? Perhaps prototype a common wrapper lib so that you can get a feeling for what a unified solution may look like?

1 Like