[Datetime-SIG] PEP-431/495

Tim Peters tim.peters at gmail.com
Tue Aug 25 04:18:42 CEST 2015


[Stuart Bishop]

I'm skipping to the end here to maintain some hope of temporal
continuity with the "Conversion vs arithmetic" thread I already spun
off:

> For amusement, here is how you can add an hour and end up exactly
> where you started. Careful you do your conversions at the right time,
> or the dst transition might eat your data (this example performed by a
> professional stuntman and should not be attempted at home):
>
> >>> from pytz.reference import Eastern
> >>> dt = datetime(2004, 4, 4, 1, 0, 0, tzinfo=Eastern)
> >>> str(dt.astimezone(timezone.utc))
>  '2004-04-04 06:00:00+00:00'
> >>> str((dt + timedelta(hours=1)).astimezone(timezone.utc))
> '2004-04-04 06:00:00+00:00'

The missing bit there:

    from datetime import datetime, timezone, timedelta

at the start.  For those who don't know (like me ;-) ),
pytz.reference.Eastern appears to be a copy/paste from the first
version of the datetime docs, showing a very simple implementation of
US daylight rules at the time it was written (transitions at 2am local
time on the first Sunday of April and the last Sunday of October).
Eastern's standard offset is -5 (and daylight -4).

So you're creating a gap time at DST start, via classic arithmetic,
and marveling ;-) at the senseless output.  The original dt is "in
standard time", and correctly maps to the UTC hour 5 hours later.
Adding an hour creates 2am standard time, which doesn't exist on the
local clock (which jumps from 1:59:59 to 3:00:00).  But since 2 is >=
2, it's considered to be in daylight time, and offset -4 maps to the
same 6am UTC.  In timeline arithmetic, the addition would have jumped
to 3am, and offset -4 would map to 7am UTC.

It would be a mistake to believe it wasn't all known a dozen years ago
that stuff like that would happen.  We were acutely aware of it.
Regardless, classic arithmetic was a deliberate design decision.  I
don't think it would help to keep repeating why, not any more than it
would help to keep showing examples where timeline and classic
arithmetic give different results.  Everyone already knows the latter,
and everyone already knows some people hate it ;-)

Why I wanted to isolate this part was for the _conversion_ question,
which is a source of surprises divorced from arithmetic.  Like so:

    u = datetime(2004, 10, 31, 5, 27, tzinfo=timezone.utc)
    print(u)
    print(u.astimezone(Eastern).astimezone(timezone.utc))

There I contrived to create a UTC time in the fold at Eastern DST end
(in the same year as your example).  Then roundtrip to Eastern and
back  The output:

2004-10-31 05:27:00+00:00
2004-10-31 06:27:00+00:00

So that's how you can do nothing at all yet end up an hour away ;-)

In that case, `u` maps to 1:27am Eastern daylight time, which is
ambiguous in Eastern.  Because it's impossible now to record that the
earlier of the ambiguous hours was picked, on the trip back it's
considered to be in standard time, so maps to the next UTC hour.

_This_ part has nothing to do with timeline arithmetic, and (as
explained in the "Conversion vs arithmetic" spinoff) will fix itself
by magic as soon as `Eastern` and `timezone.utc` tzinfos implementing
PEP 495 exist.  Conversion surprises have nothing to do with
arithmetic; they're solely due to the lack of a disambiguation bit.


More information about the Datetime-SIG mailing list