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
Make a timezone aware date and you should be back at 0.
Python astimezone() unexpected result
As you'll notice:Unfortunately using the
tzinfo
argument of the standarddatetime
constructors ‘’does not work’’ withpytz
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'
>>> 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 thelocalize()
method provided by thepytz
library.
This is used to localize a naivedatetime
(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 standardastimezone()
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
Representing and Solving a Maze Given an Image
Numpy Array Initialization (Fill with Identical Values)
Continuing in Python's Unittest When an Assertion Fails
Is There a Builtin Identity Function in Python
Disable Console Messages in Flask Server
Multiple Plots in One Figure in Python
Complete Scan of Dynamodb with Boto3
Finding Number of Colored Shapes from Picture Using Python
Make Part of a Matplotlib Title Bold and a Different Color
Building a Minimal Plugin Architecture in Python
Which Classes Cannot Be Subclassed
Importerror: Cannot Import Name Numpy_Mkl
When Should an Attribute Be Private and Made a Read-Only Property
Getting Data from Ctypes Array into Numpy
Create a Custom Transformer in Pyspark Ml