[Zope3-dev] DateTime and tzinfo signatures, etc.

Phillip J. Eby pje@telecommunity.com
Fri, 15 Mar 2002 22:02:57 -0500


Sorry I've been out of this discussion for the last couple days; I've been 
thoroughly swamped at my day job.  :)

At 10:24 AM 3/15/02 -0500, Guido van Rossum wrote:

>However, after another discussion with Jim Wednesday, we've been
>convinced to at least provide a datetime subtype that supports
>timezones in some way.
>
>One possibility is to allow date/time calculations (e.g. adding 7 days
>and 2 hours) only on naive time, and to use times with timezones
>essentially as timestamps only.
>
>Another possibility is to adopt Phillip Eby's "tzinfo object" idea.  I
>had an epiphany last night about how the tzinfo object can save
>date/time calculation semantics.  The trick is to do all calculations
>using the naive time info only, but attach the same tzinfo object to
>the result that was used for the input.  Then you could choose the
>desired semantics of your calculations by picking a tzinfo object, as
>follows:

Actually, that's what I was proposing, but I suspect that my not having 
taken the time to write a sufficiently detailed proposal left my intentions 
less than clear...



>- No tzinfo object would imply the naive result

This was the only behavior that I wasn't originally proposing; I thought of 
its usefulness later, but didn't have time to post.  :(


>- A tzinfo object representing a fixed timezone would imply the strict
>   result (which ignores DST); this would also work for UTC
>
>- A tzinfo object representing the concept of "local time in some
>   locality" would do the right thing for that locality (except that
>   there's one one per year where the results might be ambiguous)

Yep.  Let me suggest an API for the arithmetic, too, assuming immutable 
datetime objects.  Let the constructor signature be:

    dt = datetime(year,month,day,hour,minute,second,microsecond,tzinfo=None)

         OR

    dt = datetime(olddatetime, **keywordargs)


That is, you can construct a new datetime from an existing one, but modify 
any set of fields using keyword arguments.  Thus, "calendar-based" 
arithmetic is as simple as:

    dt = datetime(someDate, month=someDate.month+1)

I believe that just about all calendar-oriented arithmetic can be conducted 
in this way.  If we bring back int/float casting to UTC ticks, then 
absolute duration arithmetic is also possible.  (See below for a leap 
seconds caveat, however.)

Changing timezones when copying a datetime is a little tricky, since it 
requires conversion to ticks and back, unless either the origin or 
destination tzinfo is None, in which case no conversion is necessary and 
fields are copied as-is.

For semantic clarity, it might be best to allow only the tzinfo *or* the 
calendar fields to be changed in a single operation, by the way, since 
otherwise it's not clear whether the calendar fields should be changed 
before or after zone conversion.

Alternately, the constructor could be:

dt = datetime(tzinfo, year, month, day, hour, minute, second, microsecond)

Where tzinfo could be either a tzinfo object, an existing datetime, or 
None.  If tzinfo is a tzinfo object or None, it is used to create a new 
datetime.  If it's an existing datetime, then it's copied and any keyword 
arguments supplied are used to modify the fields.  Downside to this 
approach: the signature isn't "sideways compatible" with other Python 
datetime libraries such as mxDateTime.


>I like this idea.  I almost like it enough to make the tzinfo object
>part of the datetime base class, rather than relegating it to a
>subclass.

Ty and I are willing to supply code to read tzcode "zoneinfo" 
databases.  We propose that there be an interface for a "zoneinfo database" 
which takes tzcode time zone names (e.g. "US/Indiana") and returns a tzinfo 
object.  And, that the module supplying timezone information offer both a 
default zoneinfo database object, and an API to create a zoneinfo database 
object by specifying the path to the zoneinfo files.  In this way, one 
could use either the local platform zoneinfo database, or a standard Python 
one for maximal cross-platform compatibility.

Because the tzcode format can support leap seconds if supplied with the 
appropriate database, we suggest using UTC seconds+microseconds as an 
intermediate format during timezone conversions.  If tzinfo objects (and 
therefore datetime objects) support converting to/from UTC 
seconds+microseconds, then one can also do "absolute duration" arithmetic 
by manipulating the dates in this form.

Note that converting a date between a tzinfo object which doesn't support 
leap seconds and one which does will result in an inaccuracy, so it may be 
desirable to include a leap second flag on tzinfo objects so that an 
invalid conversion can be caught and a TypeError thrown.

I think that this approach, all in all, can be made to support the vast 
majority of use cases that have been so far expressed which deal with 
absolute moments in time.  I think also that most of the use cases that 
deal with appointments, timespans, recurring events, standalone date or 
standalone time, etc., can be built using the basic moment-in-time datetime 
objects as building blocks.