[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.