[Datetime-SIG] PEP-431/495

Stuart Bishop stuart at stuartbishop.net
Tue Aug 25 05:02:16 CEST 2015

On 25 August 2015 at 01:02, Guido van Rossum <guido at python.org> wrote:

> I'm still confused about what makes Stuart believe a tzinfo database must
> also change the arithmetic rules (especially since Gustavo's dateutils
> apparently gets along quite well without this).

A tzinfo database does not care about the arithmetic rules.

The database provided by pytz is designed in such a way that you get
correct timeline arithmetic out of Python's datetime library, by
exposing each period in the timezone definition as distinct fixed
offset tzinfo instances.

>>> from datetime import datetime, timezone, timedelta
>>> from pytz.reference import Eastern as reftz  # Tim's reference implementation
>>> import pytz
>>> dt = datetime(2004, 4, 4, 6, 59, tzinfo=timezone.utc)
>>> str(dt.astimezone(pytz.timezone('US/Eastern')))
'2004-04-04 01:59:00-05:00'
>>> str(dt.astimezone(reftz))
'2004-04-04 01:59:00-05:00'
>>> str(dt.astimezone(pytz.timezone('US/Eastern')) + timedelta(minutes=1))
'2004-04-04 02:00:00-05:00'
>>> str(dt.astimezone(reftz) + timedelta(minutes=1))
'2004-04-04 02:00:00-04:00'

So I think pytz's current implementation is entwined with the
arithmetic style. But the tzfile loader in pytz could be reworked to
present tzinfo instances suitable for classic arithmetic if you want
it in stdlib, or the parser out of dateutils used.

>> "Classic behaviour as you describe it is a bug. It sounds ok when you
>> state it as 'add one day to today and you get the same time tomorrow'.
>> It does not sound ok when you state it as 'add one second to now and
>> you will normally get now + 1 second, but sometimes you will get an
>> instant further in the future, and sometimes you will get an instant
>> in the past'."
> That sounds tautological to me -- "it's a bug because I find it buggy".

I call it a bug because adding 1 second to a moment in time and having
it incremented by an entire hour is extremely surprising behaviour.
This surprising behaviour also makes it impossible to reliably add an
absolute value to a timezone aware datetime. While people can
understand that adding a relative time period such as 1 day might get
you the same time tomorrow, it makes absolutely no sense that adding
an absolute time period like 1 second or 24 hours does not give you an
absolute answer. It is unfortunate that in this case the datetime
library interprets a timedelta as a relative period when it is
documented as being an absolute period.

It maybe documented behaviour, but the vast majority will consider it
a bug and will consider Python's datetime handling broken. At the
moment, hardly anybody is aware of the issue. I'd never even really
considered it myself - I was just trying to get my library to spit the
right answers out.

> Maybe the underlying reason is that to me, even a datetime with tzinfo does
> not refer to an instant -- it refers to something that's displayed on a
> clock. To me, arithmetic is a way of moving the hands of the clock.

In this definition, the current arithmetic fails. If you have Tim's
clock or a phone which displays the utcoffset, there are positions you
cannot move the clock hands to. In a fold you can only set the hands
to either the pre or post DST position, but not both. But you can set
the hands to invalid positions if you want to confuse people.

If, however, you stop ignoring the tzinfo you certainly can consider
arithmetic as a way of moving the hands of the clock. Add one minute,
and the minute hand goes from 59 to 0 and the hour hand increments by
one. Add one hour and the hour hand decrements and the utcoffset is
changed. All valid positions can be reached, and invalid ones
helpfully avoided.

Stuart Bishop <stuart at stuartbishop.net>

More information about the Datetime-SIG mailing list