13

I am doing some timezone conversions, and I get really weird results. Basically converting between timezones that differ only by whole hours, I still get non-whole results. For example:

from datetime import datetime
from pytz import timezone

datetime(2013, 12, 27, 20, 0, 0, tzinfo=timezone('Europe/Bucharest'))\
    .astimezone(timezone('Europe/Berlin')).replace(tzinfo=None)

gives me

datetime.datetime(2013, 12, 27, 19, 16)

(time difference between Bucharest and Berlin is 1 hour, so I should get 19:00 - instead I get 19:16)

I'm probably missing something really obvious, but I can't figure it out. What am I doing wrong?

Lukas Graf
  • 30,317
  • 8
  • 77
  • 92
ibz
  • 44,461
  • 24
  • 70
  • 86
  • Please read the comments under the accepted answer to see why it is incorrect! See the other answer instead! https://stackoverflow.com/a/45890531/1000655 – Neal Gokli May 13 '20 at 17:46

2 Answers2

11

As specified by the pytz documentation:

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

Indeed, this is not the expected result, the timezone is wrong:

>>> datetime(2013, 12, 27, 20, 0, 0, tzinfo=timezone('Europe/Bucharest'))
datetime.datetime(2013, 12, 27, 20, 0,
    tzinfo=<DstTzInfo 'Europe/Bucharest' BMT+1:44:00 STD>)

This is explained the pytz constructor given the timezone('Europe/Bucharest') timezone does not check when the timezone offset should be considered, and these things tend to change over time. pytz just uses the earlier known definition, which will often be wrong:

>>> timezone('Europe/Bucharest')
<DstTzInfo 'Europe/Bucharest' BMT+1:44:00 STD>

It looks like this timezone was used until 1931.

There is no such issue when working with UTC times and converting those using astimezone (for display purposes only as recommended):

>>> datetime(2013, 12, 27, 20, 0, 0, tzinfo=pytz.utc)\
    .astimezone(timezone('Europe/Bucharest')) 
datetime.datetime(2013, 12, 27, 22, 0,
    tzinfo=<DstTzInfo 'Europe/Bucharest' EET+2:00:00 STD>)

Then you get the expected result:

>>> datetime(2013, 12, 27, 20, 0, 0, tzinfo=pytz.utc)\
    .astimezone(timezone('Europe/Bucharest'))\
    .astimezone(timezone('Europe/Berlin'))\
    .replace(tzinfo=None)
datetime.datetime(2013, 12, 27, 21, 0)
Nicolas Cortot
  • 6,591
  • 34
  • 44
  • 3
    @ibz: the correct call is `pytz.timezone('Europe/Bucharest').localize(datetime(2013, 12, 27, 20), is_dst=None)` as said at the very beginning of [pytz docs](http://pytz.sourceforge.net/#introduction). It is wrong to use `pytz.utc` if input date is in Bucharest timezone. – jfs Jan 07 '14 at 16:22
  • @J.F.Sebastian yup, I realized that later on. The answer is wrong, because it creates a UTC time and translates that in my desired initial timezone. Which is really not the same as creating a time in my desired initial timezone! Please consider adding another answer if you care about this reputation thing. ;) – ibz Jan 24 '14 at 04:23
  • Still I think this is weird behaviour – gabn88 Aug 22 '18 at 17:27
  • @ibz: Could you select the other answer, now that it exists? I the big check mark and the ten votes may mislead people! – Neal Gokli Apr 23 '19 at 21:44
  • I *think* the big check mark and the ten (now nine) votes may mislead people :) – Neal Gokli Apr 23 '19 at 21:51
7

I came across this same issue today, and eventually solved it using the answer @jfs put in the comment of the currently accepted answer. To help anyone else finding this in the future, here is a quick example of what does and doesn't work:

from datetime import datetime
import pytz

naive = datetime.now()
la_tz = pytz.timezone("America/Los_Angeles")

# this doesn't work
with_tz = naive.replace(tzinfo=la_tz)
converted_to_utc = with_tz.astimezone(pytz.utc)
print(converted_to_utc)

# this does work
with_tz = la_tz.localize(naive)
converted_to_utc = with_tz.astimezone(pytz.utc)
print(converted_to_utc)
Chris
  • 866
  • 9
  • 19