Horario de verano en Python

Estoy escribiendo un progtwig que trata mucho de las zonas horarias y las cruzo. Las dos cosas con las que trato más son crear un objeto datetime desde “ahora” y luego localizar un objeto datetime ingenuo.

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

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

¿Es esto correcto con respecto a DST? Si no, supongo que debería estar haciendo:

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

Mi pregunta es ¿por qué? ¿Puede alguien mostrarme un caso donde el primero sea incorrecto y el segundo correcto?

En cuanto a la pregunta de los segundos, supongamos que tuve una fecha y hora ingenuas de algunos comentarios del usuario para el 1 de septiembre de 2012 a las 8:00 am en Los Angeles, CA. Es la forma correcta de hacer la fecha y hora como esta:

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

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

De la documentación de pytz :

La forma preferida de lidiar con los tiempos es trabajar siempre en UTC, convirtiéndose a la hora local solo cuando se genera una salida para que la lean los humanos.

Así que idealmente deberías usar utcnow lugar de now .

Suponiendo que, por algún motivo, sus manos están atadas y necesita trabajar en horarios locales, aún puede tener un problema al intentar localizar la hora actual si lo está haciendo durante la ventana de transición del horario de verano. La misma datetime y datetime puede ocurrir dos veces, una durante el horario de verano y otra vez durante la hora estándar, y el método de localize no sabe cómo resolver el conflicto a menos que lo indique explícitamente con el parámetro is_dst .

Así que para obtener la hora UTC actual:

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

Y para convertirlo a su hora local (pero solo cuando debe):

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

Edición: como se señaló en los comentarios de @JF Sebastian , su primer ejemplo con datetime.now(tz) funcionará en todos los casos. Su segundo ejemplo falla durante la transición de otoño como lo describí anteriormente. Todavía abogo por el uso de UTC en lugar de la hora local para todo excepto para mostrar.

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

Voy a dar un ejemplo. Aquí en Europa, al ejecutar 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)) 

Me sale lo 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 LA, más la información de la zona horaria para LA.

la.localize(datetime.now()) agrega información de la zona horaria a la hora original, pero no realiza ninguna conversión de la zona horaria; solo asume que el tiempo ya estaba en esta zona horaria.

datetime.now() crea un datetime ingenuo (sin información de zona horaria) con la hora local.

Mientras estés en Los Ángeles, no verás la diferencia, pero si tu código se ejecuta en otro lugar, probablemente no hará lo que querías.

Aparte de eso, si alguna vez necesitas lidiar seriamente con las zonas horarias, es mejor tener todos tus momentos en UTC, ahorrándote muchos problemas con el horario de verano.

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=) # show as in LA time zone (not converting here) d.astimezone(pst) >>> datetime.datetime(2016, 11, 5, 9, 43, 45, tzinfo=) # 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=) d.astimezone(pst) # now cast to LA time zone >>> datetime.datetime(2016, 11, 6, 8, 43, 45, tzinfo=) # 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=) # convert to 'America/Los_Angeles' timezone: DON'T DO THIS d = d.astimezone(pst) >>> datetime.datetime(2016, 11, 5, 9, 43, 45, tzinfo=) # 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=) # Daylight Saving is NOT respected, we are still in PDT time, not PST 

Conclusión:

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

Haz tu tiempo para sumr / restar en la zona horaria UTC SIEMPRE. Reparto a la hora local solo para salida / visualización.

El sitio web de pytz dice:

Desafortunadamente, el uso del argumento tzinfo de los constructores de fecha y hora estándar ” no funciona ” con pytz para muchas zonas horarias.

Así que no debes usar datetime.now(la) . No sé los detalles, pero algunas zonas horarias operan con reglas más exóticas de 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.