[Datetime-SIG] Are there any "correct" implementations of tzinfo?

Tim Peters tim.peters at gmail.com
Sat Sep 12 21:41:15 CEST 2015


[<random832 at fastmail.com>]
> My context is that I am working on an idea to include utc offsets in
> datetime objects (or on a similar object in a new module), as an
> alternative to something like a "fold" attribute. and since "classic
> arithmetic" is apparently so important,

Love it or hate it, it's flatly impossible to change anything about it
now, for backward compatibility.


> I'm trying to figure out how
> "classic arithmetic" _is actually supposed to work_ when adding a
> timedelta to a time lands it on the opposite side of a transition (or in
> the middle of a "spring forward" gap).

datetime arithmetic is defined in the Python docs.


> If there is a "fall back" transition tonight, then adding a day to a
> time of 12 noon today could end up as:
>
> 12 noon tomorrow, offset still DST.
> 12 noon tomorrow, offset in standard time, 25 hours from now in real
> time.
> 11 AM tomorrow, offset in standard time, 24 hours from now in real time
>
> Which one of these is "classic arithmetic"?

12 noon tomorrow in every case, regardless of tzinfo and regardless of
whether any kind of transition may or may not have occurred.  Whether
it is or isn't in DST in this specific case isn't defined by Python -
that's entirely up to what the tzinfo implementation says.  The
_intended_ way of implementing tzinfos would say it was in standard
time.


> Pytz (if you don't
> explicitly call a "normalize" function) results in something that looks
> like the first.

Yes, because pytz always uses a fixed-offset tzinfo.  There is no
difference between timeline arithmetic and classic arithmetic in any
fixed-offset zone.


> In one of the models I've thought of, you can get the
> second by replacing the tzinfo again, or the third by doing astimezone,
> but the first preserves "exactly 24 hours in the future" in both the UTC
> moment and the naive interpretation by leaving the offset alone even if
> it is an "unnatural" offset.
>
> The second one above is what you get when you call normalize.

Yes.  .normalize() effectively converts to UTC and back again  In
fact, this is all it does:

    def normalize(self, dt, is_dst=False):
        if dt.tzinfo is self:
            return dt
        if dt.tzinfo is None:
            raise ValueError('Naive time - no tzinfo set')
        return dt.astimezone(self)

.fromutc() is called as the last step of .astimezone(), and .pytz
overrides the default .fromutc() to plug "the appropriate"
fixed-offset pytz tzinfo into the result.


> My question was whether there are any real implementations that work the
> intended way.

dateutil, plus all implementations anyone may have written for
themselves based on the Python doc examples.  When datetime was
originally released, there were no concrete tzinfo implementations in
the world, so lots of people wrote their own for the zones they needed
by copy/paste/edit of the doc examples.


> If there are not, maybe the intended semantics should go
> by the wayside and be replaced by what pytz does.

Changing anything about default arithmetic behavior is not a
possibility.  This has been beaten to death multiple times on this
mailing list already, and I'm not volunteering for another round of it
;-)


More information about the Datetime-SIG mailing list