[phobos] datetime review

Jonathan M Davis jmdavisProg at gmx.com
Fri Oct 8 14:04:58 PDT 2010

Okay. Here is my current datetime code for review. It's not 100% done, but I 
think that it's ready to be reviewed. The one part that is not yet complete is 
the PosixTimeZone and WindowsTimeZone classes which allow you to get TimeZone 
objects for arbitrary time zones by name on Posix and Windows systems 
respectively. It's functionality that I definitely think should be in 
std.datetime, but it's not necessary for most programs, and this project has 
already taken me far longer than I ever anticipated, so it seems prudent to 
present my code with PosixTimeZone and WindowsTimeZone stubbed out (but 
everything else being complete) and finish them later. If the review process 
takes long enough, I may have them done before it's over anyway.

There are several TODO comments in the code which mark functionality which 
probably should be added at some point, but it's not core functionality, and I 
never intended to implement any of it before submitting my code for review (for 
instance, it would be useful to implement date recurrence patterns and be able 
to create ranges out of them - useful, but hardly core functionality and 
potentially very time-consuming to implement).

My code is based on Boost's API, but it isn't exactly the same and expands on it 
a fair bit, and none of the implementation is from Boost. It's also been 
properly D-ified, with it being highly generic and having ranges and the like. 
Since the module was pushing 40,000 lines, I split it into separate modules in a 
single package:

* datetime.all: Has a description for the package as a whole and publicly 
imports all of the other modules in datetime. Ideally, you'd just import 
datetime.all rather than trying to selectively import the other modules.

* datetime.core: Contains types and functions which are used by most of the 
other modules in datetime (e.g. the enum TUnit which hold the various units of 
time: year, hour, second, etc.).

* datetime.duration: Contains the various duration types.

* datetime.interval: Contains the various interval and range types.

* datetime.other: Contains stuff that doesn't fit in in any of the other modules.

* datetime.timepoint: Contains the various time point types. It also contains 
Clock, which will give you the current time.

* datetime.timezone: Contains the various time zone classes which SysTime uses.

Rather than Boost's date, ptime, and localtime, I have Date, TimeOfDay, 
DateTime, and SysTime.

* Date holds a Gregorian date as year, month, and day and is optimized for 
calendar operations. It has a range of approximately 32,000 BC to 32,000 AD.

* TimeOfDay holds hour, minute, and second.

* DateTime holds a Date and a TimeOfDay to have a date with the time. It's 
optimized for calendar operations and has no concept of time zone.

* SysTime is intended to be the time type to deal with time from the OS. It is 
more or less a combination of Boost's ptime and localtime. It holds the 
date/time in hecto-nanoseconds (100 ns) from midnight January 1st, 1 AD UTC and 
has a TimeZone object as part of it so that its getters can convert the time to 
that time zone. However, its internal time is always in UTC, thereby avoiding 
conversion errors due to DST. Its constructors default to using the LocalTime 
TimeZone object, so if you don't want to worry about time zones, you don't have 
to. The Clock object allows you to get a SysTime with the current time in local 
time, UTC, or any other time zone that you give it. SysTime has a range of 
approximately 29,000 BC to 29,000 AD.

One of the major changes from Boost is that I dropped special time points - that 
is positive infinity, negative infinity, and invalid (e.g. not-a-date). They 
complicated the code considerably and their only benefit, as far as I can tell, 
is to aid in defining infinite intervals and ranges. And since infinite ranges in D 
need to be known as infinite at compile time, it's not possible to define infinite 
ranges based on their end points (which are known at runtime). So, I just defined 
interval and range types which were infinite (in addition to the finite ones) and 
dropped the special time points.

Another major change is that all duration, interval, and range types (rather 
than iterator types) are generic and are intended to work with pretty much any 
time point instead of being specific to particular time point type (though there 
are some restrictions due to the fact that you can't convert between 
months/years and smaller time units due to the variable number of days in a 
month and year). Also, ranges are defined based on delegates, so they are 
extremely flexible.

I have already integrated SHOO's code from the nascent std.datetime with minimal 
alterations. Ticks became TickDuration and was put in datetime.duration. systime 
and apptime became Clock.currSystemTicks and Clock.currAppTicks respectively and 
were put in datetime.timepoint. The rest is in datetime.other (which is 
painfully small, but what's there doesn't really fit in in any of the other 
modules; however, it may grow when additional functionality is added, and it 
seems to me that the fact that the intended use is to just import datetime.all 
makes the small module less of an issue).

I didn't make much of an attempt to make my code use @safe, @trusted, or 
@system, since making that work is definitely a pain at the moment. std.array, 
std.algorithm, std.range, and std.conv (at minimum) really should be made to use 
them first. I did try and use const, nothrow, and pure liberally, though with the 
current purity rules, a lot of functions which should be pure can't be (though 
the svn version of dmd should do better), and bug 
http://d.puremagic.com/issues/show_bug.cgi?id=4974 doesn't help either.

I have included my src files as well as the ddoc html files generated from them. 
In addition to datetime, I have included a unittests module which I use heavily 
in my unit tests (and makes them much more pleasant). I'd love it if some 
version of that got into Phobos as well. My code has extensive unit tests and 
should be fully documented, so bugs should be few and far between (though I'm 
not about to claim that my code is bug-free).

The code: https://docs.google.com/leaf?id=0B-

(the hyphen after the B is part of the link; I wish that I could get my client 
to wrap the normal lines but not the link...)

- Jonathan M Davis

P.S. If anyone can figure out how  to make SysTime work as immutable, I'd really 
appreciate it. It holds a long value and an immutable TimeOfDay object in a 
Rebindable struct, so it should be able to be immutable, but I can't seem to get 
it to work, and I had to comment out all of my unit tests that verify that 
immutable SysTimes work.

Also, if anyone could tell me what the default directories for the time zone 
files on FreeBSD and OSX are, that would be very useful. It's /usr/shar/zoneinfo 
on Linux, but I have no idea where they are on FreeBSD or OSX.

More information about the phobos mailing list