[Python-Dev] datetime +/- scalars (int, long, float)?

Guido van Rossum guido@python.org
Tue, 05 Mar 2002 14:25:44 -0500


> > [MAL]
> > You should forget about DST if you want a sane 
> > implementation. Same for leap seconds.

[Kevin Jacobs]
> My datetime objects manage these complexities by layering their
> implementation.  Our base class stores a time since epoch and a
> timezone offset.  It implements all the arithmetic functions based
> on 1day=24hours and has methods that re-normalize the timezone to a
> fixed offset after every modifying operation.  It also has a
> translate(tzoffset) function that returns the an adjusted time since
> epoch with a different timezone offset.  This way, our client
> applications _never_ deal with DST or timezone changes unless they
> request it.

You can try to hide from DST, but you can't run.  It always shows its
ugly head when you lift the next stone, just as you least expected it.

I'm thinking of a layered implementation myself, and I think some big
changes to the datetime.py prototype will be necessary.  I'm thinking
that for most *business* uses of date and time, we should have the
same attitude towards DST that we've already decided to take towards
leap seconds.  If I move an appointment that's scheduled for 12 noon
today and move it a week ahead, it should still show up at 12 noon,
even if the DST changes in between.

I think I want to introduce a new concept, closely related to local
time, that I'll dub "naive time" for now.  In naive time, there is no
timezone and there is no DST.  To compensate for DST, you have to
manually change the clock, which is an action outside the system,
unknown to the system, and irrelevant to the working of the system.
Ditto to change time zones.

Naive time is what you see on your watch or your Palm organizer
(apparently not on PocketPC or Windows/CE though, which are timezone
aware).  A day is always 24 hours, and the clock always shows local
time.  When the DST jump happens, you lose or win an hour but you do
your best to pretend it didn't happen, by going to bed a little
earlier or by partying a little longer (or any other activity that
causes memory loss :-).

My Palm has no problem keeping track of appointments in different
timezones: when I have a meeting in San Francisco at 11am, I enter it
at 11am, and when I fly there, I move the Palm's clock three hours
back.  Naive time adapts to local time -- time flies (or stands still)
when you're in an airplane crossing timezones.

Naive time calculations are easier than local time calculations,
because they don't have to worry about DST.  You only have to be
careful when converting between naive time and UTC (or anything else
that has a concept of timezone).

Which brings us to...

> For calculations that require DST awareness, of which there are a
> surprising number, we have a datetime sub-class that adds two extra
> modes:
> 
>   1) Fixed tzoffset (from base datetime)
> 
>   2) Fixed tzoffset +/- fixed DST adjustment based on current time since
>      epoch (folds in to case 1 after initialization)
> 
>   3) Fixed tzoffset +/- dynamic DST based on stored time since epoch
>      (dynamically updates after every mutating date operation)

I'm not sure exactly what these mean yet, but by the time I've
implemented the new regime in datetime.py, I expect I will. :-)

I'm very tempted to put all the smarts in conversions though: you can
take a naive datetime object, interpret it in a particular timezone
using a given DST ruleset (local time and UTC being two common cases)
and convert it to a timestamp, which has different semantics, but can
be converted back to naive time by interpreting according to some
timezone.

> > If someone wants DST and timezone aware time differences,
> > then this is a completely different concept than taking the
> > difference w/r to number of days.
> 
> Precisely.  Our implementation is sub-optimal in that it can do
> roughly twice as much work as necessary at times.  This is because
> we layer the DST adjustments on top of the datetime object.  The
> advantage to this approach is that the code is very cleanly
> segregated between DST agnostic code and DST pathological code.

And that's what I want too.

> > Just to hint at another problem: you sometimes want to
> > know the time difference of two dates in terms of
> > years, months, days, hours, etc. that is, you are not
> > interested in 86400 second days, but in calendar days
> > or even months. For months you'll run into problems
> > with non-existing days, e.g. for 2002-03-31 - 1 month.
> 
> We handle these cases with a business logic layer on top of the DST
> aware datetime object.  It handles exceptions thrown, like
> BeyondEndOfMonthError, and takes the appropriate action.  In the
> case of BeyondEndOfMonthError, the typical resolution is to set the
> day to the last day of the month.

Good idea.  This can probably be remain implemented in Python.

--Guido van Rossum (home page: http://www.python.org/~guido/)