[Datetime-SIG] PEP-431/495

Akira Li 4kir4.1i at gmail.com
Thu Aug 27 16:38:33 CEST 2015

```Stuart Bishop <stuart at stuartbishop.net> writes:

...
> the complexities and slowdowns of timeline arithmetic. While not
> changing the behaviour of datetime at all, we could get cats and dogs
> living together by just clarifying what it actually is.

An *observation*: the local timezone -- the only timezone with a variable
utc offset in _stdlib -- behaves like pytz_:

utc_time = datetime(2015, 10, 25, 1, tzinfo=timezone.utc)
stdlib_time = utc_time.astimezone() # stdlib local time
pytz_time = utc_time.astimezone(tzlocal.get_localzone())
dateutil_time = utc_time.astimezone(dateutil.tz.tzlocal())

All times are consistent so far.

Perform the same operations:

>>> stdlib_time - timedelta(seconds=1)
datetime.datetime(2015, 10, 25, 1, 59, 59, tzinfo=datetime.timezone(datetime.timedelta(0, 3600), 'CET'))
>>> pytz_time - timedelta(seconds=1)
datetime.datetime(2015, 10, 25, 1, 59, 59, tzinfo=<DstTzInfo 'Europe/Paris' CET+1:00:00 STD>)
>>> dateutil_time - timedelta(seconds=1)
datetime.datetime(2015, 10, 25, 1, 59, 59, tzinfo=tzlocal())

Get different results. The times are the same but utc offset are different:

>>> (stdlib_time - timedelta(seconds=1)).utcoffset()
datetime.timedelta(0, 3600)
>>> (pytz_time - timedelta(seconds=1)).utcoffset()
datetime.timedelta(0, 3600)
>>> (dateutil_time - timedelta(seconds=1)).utcoffset() #XXX different
datetime.timedelta(0, 7200)

It is expected for arithmetic in the presense of DST transitions. Here's
the standard fix:

>>> (stdlib_time - timedelta(seconds=1)).astimezone()
datetime.datetime(2015, 10, 25, 2, 59, 59, tzinfo=datetime.timezone(datetime.timedelta(0, 7200), 'CEST'))
>>> (pytz_time - timedelta(seconds=1)).astimezone(tzlocal.get_localzone())
datetime.datetime(2015, 10, 25, 2, 59, 59, tzinfo=<DstTzInfo 'Europe/Paris' CEST+2:00:00 DST>)
>>> (dateutil_time - timedelta(seconds=1)).astimezone(dateutil.tz.tzlocal()) #XXX different
datetime.datetime(2015, 10, 25, 1, 59, 59, tzinfo=tzlocal())
>>> datetime(2015, 10, 25, 1, 59, 59, tzinfo=dateutil.tz.tzlocal()).strftime('%Z%z')
'CEST+0200'

Now the all timezones are the same but the times are different.

pytz recommends normalize() method instead of astimezone() here:

>>> tzlocal.get_localzone().normalize(pytz_time - timedelta(seconds=1))
datetime.datetime(2015, 10, 25, 2, 59, 59, tzinfo=<DstTzInfo 'Europe/Paris' CEST+2:00:00 DST>)

The result is the same in this case.

It is a _fact_. It is how python behaves now on my platform.

To try it yourself, add this at the top:

import os
import time
from datetime import datetime, timezone, timedelta
os.environ['TZ'] = 'Europe/Paris'
time.tzset()

import dateutil.tz
import tzlocal # get local timezone as pytz tzinfo
```