[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
* 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
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