2012-08-30 28 views
6

Estoy escribiendo un programa que trata mucho con husos horarios y los cruza. Las dos cosas con las que trato más son crear un objeto datetime desde "ahora" y luego localizar un objeto naive datetime.Horario de verano en Python

Para crear un objeto de fecha y hora a partir de ahora en la zona horaria del Pacífico, actualmente estoy haciendo esto (pitón 2.7.2+)

from datetime import datetime 
import pytz 
la = pytz.timezone("America/Los_Angeles") 
now = datetime.now(la) 

¿Es esto correcto en relación con el horario de verano? Si no, supongo que debería estar haciendo:

now2 = la.localize(datetime.now()) 

Mi pregunta es por qué? ¿Alguien puede mostrarme un caso donde el primero es incorrecto y los segundos son correctos?

En cuanto a la pregunta de segundos, supongamos que tengo una fecha y hora ingenua de la entrada de algunos usuarios para el 1/09/2012 a las 8:00 a.m. en Los Ángeles, CA. Es la manera correcta de hacer que el datetime como este:

la.localize(datetime(2012, 9, 1, 8, 0)) 

Si no, ¿cómo debería estar construyendo estas fechas?

Respuesta

9

Desde el pytz documentation:

La mejor forma de hacer frente a veces es trabajar siempre en UTC, la conversión a la hora local cuando la generación de salida para ser leído por los seres humanos.

Por lo tanto, idealmente debería usar utcnow en lugar de now.

Suponiendo por alguna razón que sus manos están atadas y necesita trabajar con las horas locales, todavía puede tener un problema al tratar de localizar la hora actual si lo está haciendo durante la ventana de transición de horario de verano. El mismo datetime puede aparecer dos veces, una vez durante el día y otra vez durante el horario estándar, y el método localize no sabe cómo resolver el conflicto a menos que se lo explique explícitamente con el parámetro is_dst.

Así que para obtener la corriente hora UTC:

utc = pytz.timezone('UTC') 
now = utc.localize(datetime.datetime.utcnow()) 

Y para convertirlo en su hora local (pero sólo cuando es necesario):

la = pytz.timezone('America/Los_Angeles') 
local_time = now.astimezone(la) 

Editar: como se señaló en los comentarios por @J.F. Sebastian, su primer ejemplo usando datetime.now(tz) funcionará en todos los casos. Su segundo ejemplo falla durante la transición de otoño como lo describí anteriormente. Sigo abogando por usar UTC en lugar de la hora local para todo excepto para mostrar.

+1

la forma preferible de obtener la hora actual en una zona horaria dada es: 'datetime.now (tz)'. – jfs

+0

@ J.F.Sebastian Sospecho que no funciona de manera confiable con las zonas horarias 'pytz', por las mismas razones que el constructor' datetime' no funciona con ellas. –

+1

funciona: [¿cuándo falla 'datetime.now (pytz_timezone)'?] (Http://stackoverflow.com/q/31886808/4279) – jfs

0

El pytz website dice:

desgracia con el argumento tzinfo de la fecha y hora estándar constructores ‘’ no funciona’’ con pytz para muchas zonas horarias.

Por lo tanto, no debe usar datetime.now(la). No conozco los detalles, pero algunas zonas horarias operan con reglas más exóticas que las que estamos acostumbrados, y el código de fecha y hora de Python no puede manejarlas. Al usar el código de pytz, deben manejarse correctamente, ya que ese es el propósito de pytz. También puede tener problemas para los tiempos que ocurren dos veces gracias a los tiempos de salto en el horario de verano.

En cuanto a la segunda pregunta, eso es exactamente lo que muestra la documentación, por lo que debe ser bueno.

+0

'datetime.now (tz)' es el caso, entonces funciona (no debe usar '.localize()' aquí). – jfs

+0

Debe usar datetime.datetime.utcnow(). Astimezone (tz) - Esto obtiene el tiempo en UTC y luego lo compensa desde UTC según las reglas que se apliquen en la zona horaria tz. (Pasar una zona horaria de pytz al constructor le dará una compensación que no es un número par de horas.) – jobermark

+0

@jobermark: use 'now (tz)' en lugar de 'utcnow(). Astimezone (tz)'. [Funciona] (http://stackoverflow.com/questions/12203676/daylight-savings-time-in-python/12204612#comment62276833_12204612) – jfs

5

La primera solución es correcta con respecto a DST, y la segunda solución es incorrecta.

Voy a dar un ejemplo. Aquí en Europa, cuando se ejecuta este código:

from datetime import datetime 
import pytz # $ pip install pytz 

la = pytz.timezone("America/Los_Angeles") 
fmt = '%Y-%m-%d %H:%M:%S %Z%z' 
now = datetime.now(la) 
now2 = la.localize(datetime.now()) 
now3 = datetime.now() 
print(now.strftime(fmt)) 
print(now2.strftime(fmt)) 
print(now3.strftime(fmt)) 

consigo el siguiente:

2012-08-30 12:34:06 PDT-0700 
2012-08-30 21:34:06 PDT-0700 
2012-08-30 21:34:06 

datetime.now(la) crea una fecha y hora con la hora actual en Los Ángeles, además de la información de zona horaria para LA.

la.localize(datetime.now()) agrega la información de la zona horaria a la fecha actual, pero no la conversión de la zona horaria; simplemente asume que el tiempo ya estaba en esta zona horaria.

datetime.now() crea una fecha de inicio ingenua (sin información de zona horaria) con la hora local.

Mientras se encuentre en Los Ángeles, no verá la diferencia, pero si su código se ejecuta alguna vez en otro lugar, probablemente no haga lo que usted quería.

Aparte de eso, si alguna vez necesita lidiar seriamente con los husos horarios, es mejor tener todos sus tiempos en UTC, ahorrándose muchos problemas con el horario de verano.

+0

'now2' puede devolver un resultado incorrecto (una hora de inactividad) durante el fin de Transiciones DST ("retroceso") incluso si estás en LA. 'datetime.now (la)' funciona incluso durante las transiciones UTC. – jfs

0

Esto funciona:

# naive datetime 
d = datetime.datetime(2016, 11, 5, 16, 43, 45) 
utc = pytz.UTC# UTC timezone 
pst = pytz.timezone('America/Los_Angeles') # LA timezone 

# Convert to UTC timezone aware datetime 
d = utc.localize(d) 
>>> datetime.datetime(2016, 11, 5, 16, 43, 45, tzinfo=<UTC>) 

# show as in LA time zone (not converting here) 
d.astimezone(pst) 
>>> datetime.datetime(2016, 11, 5, 9, 43, 45, 
tzinfo=<DstTzInfo 'America/Los_Angeles' PDT-1 day, 17:00:00 DST>) 
# we get Pacific Daylight Time: PDT 

# add 1 day to UTC date 
d = d + datetime.timedelta(days=1) 
>>> datetime.datetime(2016, 11, 6, 16, 43, 45, tzinfo=<UTC>) 

d.astimezone(pst) # now cast to LA time zone 
>>> datetime.datetime(2016, 11, 6, 8, 43, 45, 
tzinfo=<DstTzInfo 'America/Los_Angeles' PST-1 day, 16:00:00 STD>) 
# Daylight saving is applied -> we get Pacific Standard Time PST 

esto no funciona:

# naive datetime 
d = datetime.datetime(2016, 11, 5, 16, 43, 45) 
utc = pytz.UTC# UTC timezone 
pst = pytz.timezone('America/Los_Angeles') # LA timezone 

# convert to UTC timezone aware datetime 
d = utc.localize(d) 
>>> datetime.datetime(2016, 11, 5, 16, 43, 45, tzinfo=<UTC>) 

# convert to 'America/Los_Angeles' timezone: DON'T DO THIS 
d = d.astimezone(pst) 
>>> datetime.datetime(2016, 11, 5, 9, 43, 45, 
tzinfo=<DstTzInfo 'America/Los_Angeles' PDT-1 day, 17:00:00 DST>) 
# we are in Pacific Daylight Time PDT 

# add 1 day to LA local date: DON'T DO THAT 
d = d + datetime.timedelta(days=1) 
>>> datetime.datetime(2016, 11, 6, 9, 43, 45, 
tzinfo=<DstTzInfo 'America/Los_Angeles' PDT-1 day, 17:00:00 DST>) 
# Daylight Saving is NOT respected, we are still in PDT time, not PST 

Conclusión:

datetime.timedelta()NO cuenta para el horario de verano

Haga su tiempo sumar/restar en la zona horaria UTC SIEMPRE. Transmitido a la hora local solo para salida/visualización.

Cuestiones relacionadas