Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Python datetime + pytz issue

I'm creating a datetime object via strptime, set to "2016-01-02 03:04:05" in the "Europe/Madrid" timezone via pytz. Then I'm converting it to UTC.

Why does it add 15 minutes instead of subtract 1 hour?

>>> import datetime
>>> import pytz
>>> d = datetime.datetime.strptime('2016-01-02 03:04:05', '%Y-%m-%d %H:%M:%S')
>>> d
datetime.datetime(2016, 1, 2, 3, 4, 5)
>>> d = d.replace(tzinfo=pytz.timezone('Europe/Madrid'))
>>> d
datetime.datetime(2016, 1, 2, 3, 4, 5, tzinfo=<DstTzInfo 'Europe/Madrid' LMT-1 day, 23:45:00 STD>)
>>> d.astimezone(pytz.utc)
datetime.datetime(2016, 1, 2, 3, 19, 5, tzinfo=<UTC>)

It works correctly if instead of using "Europe/Madrid" I use "CET":

>>> d = d.replace(tzinfo=pytz.timezone('CET'))
>>> d
datetime.datetime(2016, 1, 2, 3, 4, 5, tzinfo=<DstTzInfo 'CET' CET+1:00:00 STD>)
>>> d.astimezone(pytz.utc)
datetime.datetime(2016, 1, 2, 2, 4, 5, tzinfo=<UTC>)

Edit 1: Python version is 2.7.11. pytz version is 2015.7.

Edit 2: Possible solution is to use d = pytz.timezone('Europe/Madrid').localize(d) instead of d = d.replace(tzinfo=pytz.timezone('Europe/Madrid')):

>>> d = datetime.datetime.strptime('2016-01-02 03:04:05', '%Y-%m-%d %H:%M:%S')
>>> d
datetime.datetime(2016, 1, 2, 3, 4, 5)
>>> d = pytz.timezone('Europe/Madrid').localize(d)
>>> d
datetime.datetime(2016, 1, 2, 3, 4, 5, tzinfo=<DstTzInfo 'Europe/Madrid' CET+1:00:00 STD>)
>>> d.astimezone(pytz.utc)
datetime.datetime(2016, 1, 2, 2, 4, 5, tzinfo=<UTC>)

Edit 3: Perhaps this is an instance of "using the tzinfo argument of the standard datetime constructors 'does not work' with pytz for many timezones"? Source

like image 606
Nick Jensen Avatar asked Sep 25 '22 06:09

Nick Jensen


1 Answers

Yes, the problem is in

d.replace(tzinfo=pytz.timezone('Europe/Madrid'))

where it applies the first known UTC offset in Madrid (called LMT = Local Mean Time), which was 15 minutes behind UTC (valid until 1900), or in this case expressed as -1 day +23:45:

datetime.datetime(2016, 1, 2, 3, 4, 5, tzinfo=<DstTzInfo 'Europe/Madrid' LMT-1 day, 23:45:00 STD>)

Use

pytz.timezone('Europe/Madrid').localize(d)

instead:

datetime.datetime(2016, 1, 2, 3, 4, 5, tzinfo=<DstTzInfo 'Europe/Madrid' CET+1:00:00 STD>)

which will apply the UTC offset valid in 2016, i.e. CE(S)T.

like image 99
eumiro Avatar answered Sep 29 '22 12:09

eumiro