It’s pretty typical that an app uses several different units or “granularities” of time. You might have timestamps that are in Posix/milliseconds or seconds, datepickers which have days as their smallest unit, and if you’re doing schedules you might also be using hours and/or minutes.
You could use Time.Posix
for everything, treating dates as “the first millisecond of the day” etc. but that can lead to confusion and bugs where a value that’s supposed to represent a date like “2019-06-27” gets displayed as “2019-06-26” because of timezones or daylight savings.
We’re currently using justinmimbs/date
for our dates, which avoids that problem, although we’re also using year/month pickers, and it doesn’t feel quite right to store their values as “the first day of the month”. We could of course create a custom “Period” type for this, but then we would dealing with three different time types with different interfaces.
I was thinking if there was a way to create a system that could handle all these units of time in a unified way, and thought maybe extensible records could be a solution?
Each unit of time would have a type alias like:
type alias Period r =
{ r
| year : Int
, month : Int
}
type alias Date r =
{ r
| year : Int
, month : Int
, day : Int
}
type alias Millis r =
{ r
| year : Int
, month : Int
, day : Int
, hour : Int
, minute : Int
, second : Int
, millis : Int
}
This then lets you write functions like:
diffDays : Date a -> Date b -> Int
diffDays a b =
diffMonths a b * 30 + (b.day - a.day)
diffDays
can then be used to for example compare a Date
with a Millis
. But if you tried to use it with a Period
the type system will prevent it, since Period
doesn’t include days.
Here’s the full code: https://github.com/Herteby/elm-chrono/blob/master/src/Chrono.elm
(It’s just a mockup so far, it currently assumes all months are 30 days for example :P)
But what do you think, would this be worthwhile, or should we just stick to Time
and Date
? Is this whole thing just bit of OCD and not really worth worrying about?