Problem when defining time zone using tzinfo on datetime using pytz.timezone ()

6

I found the following problem when creating an object of type datetime with the constructor parameter tzinfo that defines the time zone. To define a time zone I used the pytz module as follows:

import pytz as tz
import datetime as dt

zona_horaria = tz.timezone("Europe/Madrid")
fecha1 = dt.datetime(2017, 8, 9, 10, 10, 10)
fecha2 = dt.datetime(2017, 8, 9, 10, 10, 10, tzinfo=zona_horaria)
fecha3 = dt.datetime(2017, 8, 9, 10, 10, 10).replace(tzinfo=zona_horaria)
fecha4 = dt.datetime(2017, 8, 9, 10, 10, 10, tzinfo=tz.UTC)

The evaluation of the variables fecha give the following results:

fecha1
datetime.datetime(2017, 8, 9, 10, 10, 10)

fecha2
datetime.datetime(2017, 8, 9, 10, 10, 10, tzinfo=<DstTzInfo 'Europe/Madrid' LMT-1 day, 23:45:00 STD>)

fecha3
datetime.datetime(2017, 8, 9, 10, 10, 10, tzinfo=<DstTzInfo 'Europe/Madrid' LMT-1 day, 23:45:00 STD>)

fecha4
datetime.datetime(2017, 8, 9, 10, 10, 10, tzinfo=<UTC>)

If the variables are printed using str() we get the following results:

str(fecha1)
'2017-08-09 10:10:10'

str(fecha2)
'2017-08-09 10:10:10-00:15'

str(fecha3)
'2017-08-09 10:10:10-00:15'

str(fecha4)
'2017-08-09 10:10:10+00:00'

As can be seen, the time offset of fecha2 and fecha3 is only -15 minutes compared to UTC, when it should be +2: 00 for the Madrid time. If it is checked with timestamp , the following results are obtained:

fecha1.timestamp()
1502266210.0

fecha2.timestamp()
1502274310.0

fecha3.timestamp()
1502274310.0

fecha4.timestamp()
1502273410.0

That is, the difference between fecha2 or fecha3 and fecha4 is 900 seconds (15 minutes), when it should be 2 hours. How can I create a datetime start object by assigning it a time zone and that the correct phase shift is registered?

PD: If you create an object type datetime using fromtimestamp() it works, but you have to know before the timestamp UTC corresponding to the local date you want to create:

dt.datetime.fromtimestamp(timestamp, zona_horaria)
    
asked by Carlos A. Gómez 09.08.2017 в 23:02
source

2 answers

3

The pytz module, in the localize() method of the objects that represent the time zone (implementation of the abstract class datetime.tzinfo ), it is recommended not to use the parameter tzinfo of the constructor datetime.datetime :

    This method should be used to construct localtimes, rather
    than passing a tzinfo argument to a datetime constructor.

Therefore, to create datetime.datetime objects with a certain time zone, the correct way to avoid errors would be to call the method localize() of the object that represents the time zone obtained through the module pytz (the condition is that the datetime.datetime object passed by argument must have tzinfo=None ).

The results of the evaluation of the obtained objects are:

zona_horaria.localize(fecha1)
datetime.datetime(2017, 8, 9, 10, 10, 10, tzinfo=<DstTzInfo 'Europe/Madrid' CEST+2:00:00 DST>)

zona_horaria.localize(fecha2)
ValueError: Not naive datetime (tzinfo is already set)

zona_horaria.localize(fecha3.replace(tzinfo=None))
datetime.datetime(2017, 8, 9, 10, 10, 10, tzinfo=<DstTzInfo 'Europe/Madrid' CEST+2:00:00 DST>)

tz.UTC.localize(fecha4.replace(tzinfo=None))
datetime.datetime(2017, 8, 9, 10, 10, 10, tzinfo=<UTC>)

Now you can see how the time zone is defined correctly taking into account the summer time.

If we check the timestamp it gives us the following results.

tz.UTC.localize(fecha4.replace(tzinfo=None)).timestamp()
1502273410.0

zona_horaria.localize(fecha3.replace(tzinfo=None)).timestamp()
1502266210.0

Giving a correct difference of 2 hours (7200 seconds).

    
answered by 09.08.2017 в 23:02
1

Another option is to simply use the astimezone method:

>>> import datetime as dt
>>> import pytz as tz
>>> fecha = tz.utc.localize(dt.datetime(2017, 8, 9, 10, 10, 10))
>>> print(fecha)
2017-08-09 10:10:10+00:00
>>> print(fecha.astimezone(tz.timezone('Europe/Madrid')))
2017-08-09 12:10:10+02:00
>>> print(fecha.astimezone(tz.timezone('America/Lima')))
2017-08-09 05:10:10-05:00
>>> print(fecha.astimezone(tz.timezone('America/Los_Angeles')))
2017-08-09 03:10:10-07:00

If you already have the tzinfo defined you can replace UTC and then apply astimezone :

>>> zona_horaria = tz.timezone("Europe/Madrid")
>>> fecha = dt.datetime(2017, 8, 9, 10, 10, 10, tzinfo=zona_horaria)
>>> print(fecha)
2017-08-09 10:10:10-00:15
>>> fecha = fecha.replace(tzinfo=tz.timezone('UTC'))
>>> print(fecha) # UTC
2017-08-09 10:10:10+00:00
>>> print(fecha.astimezone(tz.timezone('Europe/Madrid')))
2017-08-09 12:10:10+02:00

It is a good alternative for the localize commented by @ CarlosA.Gómez in your answer.

    
answered by 09.08.2017 в 23:22