This post is meant to be the first in a series highlighting various interesting features of Core (although I should acknowledge that most of the continuing series I’ve started so far have not, when it comes down to it, continued.) This time I wanted to focus on how Core handles time. Time is a surprisingly complex topic, as anyone who has worked through the gory details of calendrical calculations can tell you. One of the initial complexities is that there are lots of different-but-related concepts that come into play, and in order to design a good library, you have to figure out how to reflect those concepts in your datatypes. Here are the primary types that

Core uses for dealing with time:

  • Time.t, an absolute time, i.e., a fully specified point in time, independent of time zone or any other information.
  • Time.Span.t, a length of time, as in “5 minutes” or “3 hours”.
  • Time.Ofday.t, a time of day, as in, “3:53:12 PM”.
  • Date.t, a date, e.g. “2008-12-13”.
  • Weekday.t, a day of the week, e.g., “Monday”
  • TZ.Zone.t, a timezone, e.g., “EST5EDT”. The combination of a date, a time of day, and a timezone is sufficient to compute a Time.t

Interestingly, Time.t, Time.Ofday.t and Time.Span.t share the same underlying implementation: they are all floating point numbers representing a number of seconds. A Time.t is the same kind of float returned by the gettimeofday call in the standard of library, basically a traditional UNIX time. A Time.Ofday.t is implemented with a float representing the number of seconds since the beginning of the day, and a Time.Span.t is represented by a float representing the number of seconds in question.

By seperating into three types rather than one, we get types that are more informative and less error prone. For example, the function Time.diff and Time.add have the following signatures:

val diff: Time.t -> Time.t -> Time.Span.t
val add: Time.t -> Time.Span.t -> Time.t

This stops you from making basic mistakes, like taking two absolute times and adding them together and expecting to get another absolute time.

Core’s handling of time has a lot going for it. There are many useful functions, and it’s been reasonably well battle tested (although there are surely bugs yet to be found.) There is also the very useful TZ module, which is a reimplementation of UNIX’s timezone handling, which uses the standard UNIX timezone database. (“Why reimplement?” you may ask. It turns out that the libc calls for doing timezone conversion require you to specify the timezone by stuffing it into the TZ environment variable before making the call. That makes these calls painful and error-prone in the presence of threads.)

The biggest remaining problem we have is that time zone handling is not integrated into the Time module itself – Time can only convert strings in localtime and UTC. Integration of TZ into Time is something we hope to get done in the next release or two.