Unexpected Results Converting Timezones in Python

unexpected results converting timezones in python

From the partial documentation:
http://pytz.sourceforge.net/#localized-times-and-date-arithmetic

Unfortunately using the tzinfo argument of the standard datetime constructors ‘’does not work’’ with pytz for many timezones. [...] It is safe for timezones without daylight saving transitions though, such as UTC. [...] The preferred way of dealing with times is to always work in UTC, converting to localtime only when generating output to be read by humans.

Unexpected result when converting datetime(1970,1,1) to seconds

According to the docs: Naive datetime instances are assumed to represent local time

  • https://docs.python.org/3/library/datetime.html

  • timestamp: https://docs.python.org/3/library/datetime.html#datetime.datetime.timestamp

You get a negative count so I guess you happen to be at UTC-3 (10800 / 60 / 60) (and you get a 0 when adding 3 hours)

Make a timezone aware date and you should be back at 0.

Python astimezone() unexpected result

Unfortunately using the tzinfo argument of the standard datetime
constructors ‘’does not work’’ with pytz for many timezones.

>>> datetime(2002, 10, 27, 12, 0, 0, tzinfo=amsterdam).strftime(fmt)
'2002-10-27 12:00:00 LMT+0020'

It is safe for timezones without daylight saving transitions though, such as UTC:

>>> datetime(2002, 10, 27, 12, 0, 0, tzinfo=pytz.utc).strftime(fmt)
'2002-10-27 12:00:00 UTC+0000'

As you'll notice:

>>> datetime.datetime(2000, 1, 1, 0, 1, tzinfo=pytz.timezone('Europe/Paris'))
datetime.datetime(2000, 1, 1, 0, 1, tzinfo=<DstTzInfo 'Europe/Paris' LMT+0:09:00 STD>)

"LMT+0:09:00 STD"…?! That's a historical offset, not a current standard.

The timezone bundles (containing all historical offsets since forever) returned by pytz aren't handled correctly by datetime, and it chooses some random (well, the first probably) offset instead of the offset pertinent to the actual time. Arguably, since it needs to interpret the time correctly first it cannot choose the right offset by time from the timezone bundle.

This library only supports two ways of building a localized time. The
first is to use the localize() method provided by the pytz library.
This is used to localize a naive datetime (datetime with no timezone
information):

>>> loc_dt = eastern.localize(datetime(2002, 10, 27, 6, 0, 0))
>>> print(loc_dt.strftime(fmt))
2002-10-27 06:00:00 EST-0500

The second way of building a localized time is by converting an existing
localized time using the standard astimezone() method:

>>> ams_dt = loc_dt.astimezone(amsterdam)
>>> ams_dt.strftime(fmt)
'2002-10-27 12:00:00 CET+0100'

http://pytz.sourceforge.net

Adding timedelta to local datetime, unexpected behaviour accross DST shift

The rationale is : timedelta arithmetic is wall time arithmetic. That is, it includes the DST transition hours (or excludes, depending on the change). See also P. Ganssle's blog post on the topic .

An illustration:

import datetime as dt
from zoneinfo import ZoneInfo

# Midnight
d0 = dt.datetime(2020, 3, 29, 0, 0, tzinfo=ZoneInfo("Europe/Paris"))

for h in range(1, 4):
print(h)
print(d0 + dt.timedelta(hours=h))
print((d0 + dt.timedelta(hours=h)).astimezone(ZoneInfo("UTC")), end="\n\n")
1
2020-03-29 01:00:00+01:00
2020-03-29 00:00:00+00:00 # as expected, 1 hour added

2
2020-03-29 02:00:00+01:00 # that's a non-existing datetime...
2020-03-29 01:00:00+00:00 # looks normal

3
2020-03-29 03:00:00+02:00
2020-03-29 01:00:00+00:00 # oops, 3 hours timedelta is only 2 hours actually!

Need more confusion? Use naive datetime. Given that the tz of my machine (Europe/Berlin) has the same DST transitions as the tz used above:

d0 = dt.datetime(2020, 3, 29, 0, 0)

for h in range(1, 4):
print(h)
print(d0 + dt.timedelta(hours=h))
print((d0 + dt.timedelta(hours=h)).astimezone(ZoneInfo("UTC")), end="\n\n")
1
2020-03-29 01:00:00 # 1 hour as expected
2020-03-29 00:00:00+00:00 # we're on UTC+1

2
2020-03-29 02:00:00 # ok 2 hours...
2020-03-29 00:00:00+00:00 # wait, what?!

3
2020-03-29 03:00:00
2020-03-29 01:00:00+00:00

How to make a timezone aware datetime object

In general, to make a naive datetime timezone-aware, use the localize method:

import datetime
import pytz

unaware = datetime.datetime(2011, 8, 15, 8, 15, 12, 0)
aware = datetime.datetime(2011, 8, 15, 8, 15, 12, 0, pytz.UTC)

now_aware = pytz.utc.localize(unaware)
assert aware == now_aware

For the UTC timezone, it is not really necessary to use localize since there is no daylight savings time calculation to handle:

now_aware = unaware.replace(tzinfo=pytz.UTC)

works. (.replace returns a new datetime; it does not modify unaware.)



Related Topics



Leave a reply



Submit