[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_:

  # start with the same utc time
  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


More information about the Datetime-SIG mailing list