Eric said:
Here are two things I've observed:
1) Every date/time package I've ever run across in any
programming language has been deficient in one way or another,
sometimes to the point of looking utterly stupid, and
2) Many of those date/time packages were designed and
implemented by people a whole lot smarter than I am.
From these observations, I draw two conclusions:
A) The ways people reckon dates and times are far more
complicated than a naive glance suggests, and
B) It's folly to throw words like "stupid" around. (Or
stupid to throw words like "folly" around; I can never keep
that straight.)
As examples of (A), consider "same time next month" when
uttered on January 30, or "same time next year" when uttered
on February 29. Even "same time tomorrow" will be tricky in
my locale for most of this Saturday.
I can sum up the problem in one quick sentence: failure to separate two
areas of responsibility, "internal representation and arithmetic" and
"UI presentation". The former can be done very cleanly, using either a
standard Gregorian calendar in the UTC+0000 time-zone or just using
seconds since the epoch or whatever. The latter then boils down to
converting between a locale-dependent representation and the internal one.
Systems with such a division can potentially do a very good job of
avoiding Y2K bugs, bugs when the hour gets set back in the fall, and
similar problems. In particular, when internal times are stored in a
fixed time zone, or in seconds since the epoch, the internal clock never
jumps backwards by an hour and no two events have the same timestamp
without being genuinely (close to) simultaneous. Only the display in the
UI is affected by DST transitions and such.
DST transitions end up having either of two interesting UI effects.
One would be for the date/time combos after the transition to be
affected in their presentation, which could result in e.g. an
interactive program listing or appointment-book app showing
11:00 PM 12:00 PM 1:00 AM 1:00 AM 2:00 AM
with different things under the two "1:00 AM" columns. Or something in
the same general vein. Different hours with the same displayed name. If
the user can click to manipulate objects, and not just get a "handle" on
them by typing a name, it won't cause too much trouble.
The other would be to have the presentation of ALL dates switch. A
simple switch-the-system-locale-at-DST-transition system would cause
this, and then the display above would go from 11 until 3, except that
if you did something after the DST transition to make it redraw itself,
it would then show 10 until 2. At neither time would two hours have the
same name, but you could set something to 2 PM the next afternoon in
such an interface and have it change overnight to be at 1 PM, which is
why I'd favor the first method for handling DST over the second.
(The forward transition in the spring would have similar, but milder
effects. In the second case, that 2PM appointment would jump to 3PM,
which is equally bad. In the first case, the displayed hour names would
skip over one name you'd normally expect, rather than duplicate one.
Here is where the effect is milder, because typing in a time around
"spring forward" won't sometimes be ambiguous unlike in the "fall back"
case. But a UI that lets the user click on objects to manipulate them
without having to type in their names does not suffer much from such
ambiguity anyway.)
Date arithmetic would yield simple and predictable results with a proper
separation of content and presentation concerns: adding 30 days, for
example, would move Jan. 30 to Feb. 29 on leap years and Mar. 1
otherwise. If there was an "add one month", it would have to cope
somehow (say, by snapping the day to the last day of the month if it
would otherwise go over) and its behavior at corner cases would have to
be properly documented. (That results in Jan. 30 plus 1 month being
either Feb. 29 or Feb. 28, depending on the leap-year status of the year
of the date being manipulated.)
Often, the application may need a bit of its own localized logic to deal
with figuring out what is the "next business day" or similarly. This
should be cleanly separated from the core Date class that concerns
itself with merely representing dates and times and with "next day" and
similarly.
Possibly, cleanest would be to have seconds (or milliseconds, or
whatever) since the epoch as the core time type's representation and all
notions of months, days, years, or other such be in the presentation
classes rather than the core Time class. One might even then have a Date
class that is polymorphic and localized, and that wraps a Time object.
It could be set from a Time object and its Time object retrieved, as
well as have formatting, parsing, and other methods and methods like
addDays. You'd also want Duration objects, built on an integer, with
DateDifference the localized wrapper. Date subtraction would produce
DateDifference objects, and DateDifferences could be summed or
subtracted, multiplied by scalar integers, and added to or subtracted
from Dates, for any arithmetic involving fixed intervals. Under the
hood, long arithmetic is being performed. Date methods might also exist
to add "n months", "n years", or similarly, that deal with Feb. 29
appropriately, and perhaps even a "nextBusinessDay" method could exist,
given the locale objects were specific and detailed enough to have each
area's local holidays and business practises mapped. Some areas have
Friday and Saturday as the weekend and a Saturday sabbath, for example.
States in the US have civic holidays other states lack.
At least all of the "messy" bits would be swept into a class whose
responsibility was to deal with them and to manage presentation and
user-input of dates, while simple under-the-hood timekeeping would be
managed by something clean and simple.
I think they tried to do something like that with Date/Calendar, but
ended up giving Calendar the job of being Date's primary builder as
well, instead of Date having its own simpler means of constructing it
that a) used the default international standard representations, a
Gregorian calendar in the GMT timezone as I understand it, and b) wasn't
deprecated.