[Zope3-dev] DateTime, zones, and arithmetic

Phillip J. Eby pje@telecommunity.com
Tue, 12 Mar 2002 10:20:08 -0500


At 07:49 PM 3/11/02 -0500, Guido van Rossum wrote:

>I expect that something like this will be a standard library module in
>Python 2.3.  Now is the time to give feedback.  Note that the latest
>design ideas are in the "NaiveTime" part of the Wiki -- this means
>dropping the timezone from the datetime representation, because it
>makes decent time arithmetic (e.g. adding whole N days to a datetime)
>impossible.  But it's easy to add the timezone info in a subclass --
>if you promise not to do time arithmetic or not to care how it
>interoperates with DST transitions.  (Read the NaiveTime page before
>commenting on this statement.)

There's a way to fix the zone problem that you encountered - using a zone 
*object*, rather than a simple time zone offset.  The library Ty and I use 
(we've been maintaining/upgrading Ted Horst and Keith Waclena's old Julian 
package for Python 1.3!) has timezone objects which are responsible from 
converting a broken-out time (Y,M,D,h,m,s) into a Unix-style UTC-based tick 
count, and vice versa.  The date/time value delegates these operations to 
the zone object.  You can thus do absolute duration arithmetic on the tick 
count, *and* relative calendar arithmetic on the date fields.

Ironically, Ty and I have found this rather ancient library to be a far 
superior datetime solution than *any* other datetime implementation for 
Python we've investigated, since it includes fully generic parsing and 
formatting as well as both kinds of calendar arithmetic and complete, 
*correct* timezone support.

The datetime objects it provides are mutable, so calendar shifting is done 
by modifying fields, e.g. 'someDateTime.month += 3' to shift by three 
months.  (This is implemented by creating a modified Y,M,D,h,m,s structure, 
then asking the zone object to normalize the fields and convert to a ticks 
value.)  Of course, if immutable datetime objects are *really* needed, then 
datetime deltas along the lines of s.keim and hkrosing's comments in the 
Wiki might be more appropriate.

One of the nice things about a mutable datetime, however, is that it makes 
certain kinds of calendar operations trivial.  For example::

    someDate = datetime() # default to today
    someDate.day = 1      # first of this month
    someDate.month += 1   # first of next month

These kinds of manipulation are common in our applications that do 
reporting and need to offer the user default date ranges.  Also, changing 
the zone object of a datetime in our usage lets us easily convert a 
datetime to the zone of the application user.  (We report all event 
timestamps converted to the viewer's local timezone, from the standard zone 
used in the database.)

By contrast, I've never had a need to use a date/time value as a dictionary 
key.  As a key in a sorted list, once or twice, and in those cases I needed 
a ticks value and didn't care about the other fields.  But if immutability 
is a hard requirement, perhaps there could be an "offset()" method which 
took keyword arguments and returned a new value, e.g.::

    someDate = datetime()
    newDate = someDate.offset(day=1, month=someDate.month+1)

If "zone" were one of the arguments offset() could take, then we could also 
do our zone conversions easily.

Would it be worthwhile to make a formal proposal for zone objects (for 
either mutable or immutable datetime objects)?  A "localtime" zone object 
is relatively straightforward to implement, without reference to a timezone 
database.  However, the library we're maintaining has code that reads a BSD 
zone database to create zone objects, so we can support zones like 
'US/Eastern' and the like, complete with DST handling.  Notice also, by the 
way, that the notion of a zone can actually be an entire calendar unto 
itself, if someone needs off-the-wall calendars.

Or, do you see this as something that folks who want zones should just do 
in a subclass and leave the core implementation "naive" wrt zones?