Rant: Date and Time fall short of simplicity in D

Andrej Mitrovic andrej.mitrovich at gmail.com
Thu Mar 28 22:42:41 PDT 2013


import core.thread;
import core.time;
import std.datetime;
import std.stdio;

void main()
{
    StopWatch sw;
    sw.start();
    Thread.sleep(dur!"seconds"(1));
    Thread.sleep(dur!"msecs"(200));
    Thread.sleep(dur!"usecs"(800));
    sw.stop();

    TickDuration time = sw.peek();

    long secs = time.seconds;
    long msecs = time.msecs - (1000 * secs);
    long usecs = time.usecs - (1000 * msecs) - (1000 * 1000 * secs);

    writefln("secs: %s, msecs: %s, usecs: %s", secs, msecs, usecs);
}

The above works, but it's really messy having to do this all by hand.
So the other workaround is:

    TickDuration time = sw.peek();
    long secs = time.seconds;
    time -= cast(TickDuration)dur!"seconds"(secs);

    long msecs = time.msecs;
    time -= cast(TickDuration)dur!"msecs"(msecs);

    long usecs = time.usecs;

Note that the casts are necessary since there is no "tickdur!()"
function and a TickDuration cannot be subtracted with a Duration (I
don't understand why it's implemented like this). The whole date and
time API is extremely non orthogonal and ugly in D.

For example, Duration has seconds, msecs, usecs as property functions
but they are all calculated as the duration minus the larger units, so
1.200 seconds means that time.seconds == 1 && time.msecs == 200. But
TickDuration uses a different API where time.seconds == 1 &&
time.msecs == 1200.

Why was this inconsistency introduced?

--

The documentation for various property functions was clearly
copy-pasted, take a look for example at these:

    /++
        The value of this $(D FracSec) as milliseconds.
      +/
    @property int msecs() @safe const pure nothrow;

    /++
        The value of this $(D FracSec) as milliseconds.

        Params:
            milliseconds = The number of milliseconds passed the second.

        Throws:
            $(D TimeException) if the given value is not less than $(D 1) second
            and greater than a $(D -1) seconds.
      +/
    @property void msecs(int milliseconds) @safe pure;

The second property function is a setter, it should be documented as:

"Sets the value of this $(D FracSec) to $(B milliseconds) number of
milliseconds."

I've seen this type of property documentation copied everywhere in
date/time-related structures and classes, it's not clear from the
documentation that these are setters, they should be documented as
such.

---

Another example, I once had to convert a long type which represented
Unix time into DateTime. Here's the code to do it:

return cast(DateTime)SysTime(unixTimeToStdTime(cast(int)d.when.time));

It's unbelievable how ugly this is, and it took me way over half an
hour searching the docs and trying various stuff out to figure this
out.

---

Anyway, maybe time and datetime are power-houses in Druntime and
Phobos, but they trade their features for simplicity.

Perhaps the real problem is the documentation, or the actual layout of
the API itself. The API seems to contain a ton of functionality, and
maybe the more specialized functions should be moved into separate
modules (and make datetime be part of its own package).

The docs for std.datetime for example are huge.


More information about the Digitalmars-d mailing list