¿Por qué datetime.datetime.utcnow () no contiene información de zona horaria?

285
datetime.datetime.utcnow()

¿Por qué esto datetimeno tiene información de zona horaria dado que es explícitamente un UTC datetime?

Esperaría que esto contenga tzinfo.

Vitaly Babiy
fuente
¿Cómo convertir un campo de fecha de formato iso normal que es de tipo cadena a formato utc?
Navi

Respuestas:

192

Eso significa que es una zona horaria ingenua, por lo que no puede usarla con datetime.astimezone

puedes darle una zona horaria como esta

import pytz  # 3rd party: $ pip install pytz

u = datetime.utcnow()
u = u.replace(tzinfo=pytz.utc) #NOTE: it works only with a fixed utc offset

ahora puedes cambiar las zonas horarias

print(u.astimezone(pytz.timezone("America/New_York")))

Para obtener la hora actual en una zona horaria determinada, puede pasar tzinfo datetime.now()directamente a:

#!/usr/bin/env python
from datetime import datetime
import pytz # $ pip install pytz

print(datetime.now(pytz.timezone("America/New_York")))

Funciona para cualquier zona horaria, incluidas las que observan el horario de verano (DST), es decir, funciona para zonas horarias que pueden tener diferentes compensaciones de utc en diferentes momentos (compensación de utc no fija). No lo use tz.localize(datetime.now()): puede fallar durante la transición de finalización del horario de verano cuando la hora local es ambigua.

John La Rooy
fuente
216
Pero no hay una buena razón para que sea ingenua en la zona horaria: se especifica que es UTC. ¿Por qué necesita buscar una biblioteca de terceros para que funcione correctamente?
Mark Ransom
44
Estoy de acuerdo; para mí los tiempos "ingenuos" son completamente inútiles. Hay una discusión en la lista de python en este momento acerca de agregar pytz a stdlib; El problema no es la concesión de licencias, sino el hecho de que los datos de la zona horaria se actualizan con tanta frecuencia (que Python no puede ser) Además, pytz no implementa la interfaz tzinfo de la manera esperada, por lo que puede obtener errores si intenta usar algunas de las zonas horarias de la ciudad astimezone. Por lo tanto, datetime no solo no tiene zonas horarias nativas, sino que la única implementación ampliamente disponible de tzinfo no cumple con el supuesto estándar.
bobince
55
@bobince ¿Por qué Pytz y las bibliotecas de fecha y hora estándar no funcionan para usted? El núcleo de Python y Pytz evolucionando como proyectos independientes reduce la complejidad logística para el equipo central. Sí, reducir la complejidad para el equipo central de Python aumenta la complejidad para todos los usuarios de Python que necesitan lidiar con zonas horarias, pero confío en que tomaron esta decisión por una buena razón. La regla "La biblioteca estándar no tiene instancias tzinfo ..." es genial porque es simple, ¿por qué hacer una excepción aquí?
Derek Litz
15
¿Qué tal solou=datetime.now(pytz.utc)
Craig McQueen
44
@bain: no usar tz.localize(datetime.now()); utilizar datetime.now(tz)en su lugar.
jfs
142

Tenga en cuenta que para Python 3.2 en adelante, el datetimemódulo contiene datetime.timezone. La documentación para datetime.utcnow()dice:

Se puede obtener una fecha y hora UTC actual al llamar .datetime.now(timezone.utc)

Entonces puedes hacer:

>>> import datetime
>>> datetime.datetime.now(datetime.timezone.utc)
datetime.datetime(2014, 7, 10, 2, 43, 55, 230107, tzinfo=datetime.timezone.utc)
Craig McQueen
fuente
2
¿Cuál es el preferido? datetime.now(timezone.utc)o datetime.utcnow(timezone.utc)?
Jesse Webb
8
datetime.utcnow()No toma argumentos. Entonces tendría que ser así datetime.now(timezone.utc).
Craig McQueen
1
datetime.now()devolverá la hora de la máquina pero datetime.utcnow()devolverá la hora UTC real.
Babu
13
@Babu: datetime.utcnow()no está configurado tzinfopara indicar que es UTC. Pero datetime.now(datetime.timezone.utc)devuelve la hora UTC con tzinfo set.
Craig McQueen
@CraigMcQueen Entonces, si pasamos un tzobjeto en el constructor ahora, ¿devolverá la hora de esa zona horaria? ¡Okay! Gracias por señalarlo.
Babu
71

Las bibliotecas estándar de Python no incluyen ninguna clase de tzinfo (pero vea pep 431 ). Solo puedo adivinar las razones. Personalmente, creo que fue un error no incluir una clase tzinfo para UTC, porque esa no es lo suficientemente controvertida como para tener una implementación estándar.

Editar: aunque no hay implementación en la biblioteca, hay una dada como ejemplo en la tzinfodocumentación .

from datetime import timedelta, tzinfo

ZERO = timedelta(0)

# A UTC class.

class UTC(tzinfo):
    """UTC"""

    def utcoffset(self, dt):
        return ZERO

    def tzname(self, dt):
        return "UTC"

    def dst(self, dt):
        return ZERO

utc = UTC()

Para usarlo, para obtener la hora actual como un objeto de fecha y hora consciente:

from datetime import datetime 

now = datetime.now(utc)

Hay datetime.timezone.utcen Python 3.2+:

from datetime import datetime, timezone 

now = datetime.now(timezone.utc)
Mark Ransom
fuente
8
Averigüe por qué esta clase no se proporcionó en primer lugar (y, lo que es más importante, se usó para datetimeobjetos creados por utcnow()) ...
André Caron
17
El objeto de zona horaria timezone.utcfinalmente se ha agregado a Python 3.2. Para la compatibilidad con versiones anteriores, utcnow()aún devuelve un objeto de tiempo sin zona horaria, pero puede obtener lo que desea llamando now(timezone.utc).
mhsmith
44
@rgove, ese es el tipo de corrección de errores que se suponía que era un juego justo para Python 3. No deberían haberse preocupado por la compatibilidad con versiones anteriores. Hay otro ejemplo que leí en los últimos días: el structmódulo haría conversiones automáticas de Unicode a bytestring, y la decisión final fue romper la compatibilidad con versiones anteriores de Python 3 para evitar que una mala decisión avance.
Mark Ransom
2
Me sorprende que la tzinfodocumentación de Python incluya ejemplos de código para implementarlo, ¡pero no incluyen esa funcionalidad en fecha y hora! docs.python.org/2/library/datetime.html#datetime.tzinfo.fromutc
LS
1
@LS sí, pytzes un gran recurso. Cuando edité mi respuesta para poner el código de ejemplo, otra persona ya lo había sugerido y no quería robarles el trueno.
Mark Ransom
20

El pytzmódulo es una opción, y hay otra.python-dateutil , que aunque también es un paquete de terceros, ya puede estar disponible dependiendo de sus otras dependencias y sistema operativo.

Solo quería incluir esta metodología como referencia; si ya la instaló python-dateutilpara otros fines, puede usarla en tzinfolugar de duplicarla conpytz

import datetime
import dateutil.tz

# Get the UTC time with datetime.now:
utcdt = datetime.datetime.now(dateutil.tz.tzutc())

# Get the UTC time with datetime.utcnow:
utcdt = datetime.datetime.utcnow()
utcdt = utcdt.replace(tzinfo=dateutil.tz.tzutc())

# For fun- get the local time
localdt = datetime.datetime.now(dateutil.tz.tzlocal())

Tiendo a aceptar que las llamadas a utcnowdeben incluir la información de la zona horaria UTC. Sospecho que esto no está incluido porque la biblioteca nativa de fecha y hora por defecto es fecha y hora ingenua para compatibilidad cruzada.

bbengfort
fuente
1
NameError: el nombre 'dt' no está definido
xApple
Estaba usando la llamada datetime.datetime.utcfromtimestamp (), y necesitaba agregar tzinfo, la segunda solución funcionó para mí: utcdt = datetime.datetime.utcfromtimestamp(1234567890).replace(dateutil.tz.tzutc())
Ian Lee
1
nota: a diferencia de datetime.now(pytz_tz)eso siempre funciona; datetime.now(dateutil.tz.tzlocal())puede fallar durante las transiciones DST . PEP 495 - La desambiguación de la hora local podría mejorar la dateutilsituación en el futuro.
jfs
@IanLee: podría usar utc_dt = datetime.fromtimestamp(1234567890, dateutil.tz.tzutc())(nota: dateutilcon un desplazamiento utc no fijo (como dateutil.tz.tzlocal()) puede fallar aquí , use una pytzsolución basada en su lugar ).
jfs
Desde mi programa ya estaba importando dateutilpara dateutil.parser, me ha gustado esta solución mejor. Era tan simple como: utcCurrentTime = datetime.datetime.now(tz=dateutil.tz.tzutc()). ¡¡Viola!!
LS
11

Julien Danjou escribió un buen artículo explicando por qué nunca debes tratar con zonas horarias . Un experto:

De hecho, Python datetime API siempre devuelve objetos datetime inconscientes, lo cual es muy desafortunado. De hecho, tan pronto como obtienes uno de este objeto, no hay forma de saber cuál es la zona horaria, por lo tanto, estos objetos son bastante "inútiles" por sí mismos.

Por desgracia, aunque pueda usarlo utcnow(), no verá la información de la zona horaria, como descubrió.

Recomendaciones:

  • Utilice siempre datetimeobjetos conscientes , es decir, con información de zona horaria. Eso asegura que pueda compararlos directamente (los datetime objetos conscientes e inconscientes no son comparables) y los devolverá correctamente a los usuarios. Aproveche pytz para tener objetos de zona horaria.

  • Utilice ISO 8601 como formato de cadena de entrada y salida. Se usa datetime.datetime.isoformat()para devolver marcas de tiempo como cadenas formateadas con ese formato, que incluye la información de la zona horaria.

  • Si necesita analizar cadenas que contienen marcas de tiempo con formato ISO 8601, puede confiar iso8601, lo que devuelve marcas de tiempo con la información de zona horaria correcta. Esto hace que las marcas de tiempo sean directamente comparables.

Joe D'Andrea
fuente
1
Esta es una recomendación ligeramente engañosa. La regla general es, nunca trate con zonas horarias. Siempre almacene y transmita tz objetos utc no conocidos (objetos de época). La zona horaria solo debe calcularse en el momento de la representación en la interfaz de usuario
nehem
1
Parece que ya coincide con los pensamientos de Julien bastante bien. ¿Cuáles de sus recomendaciones específicas (como se mencionó anteriormente) son engañosas?
Joe D'Andrea
10

Para agregar timezoneinformación en Python 3.2+

import datetime

>>> d = datetime.datetime.now(tz=datetime.timezone.utc)
>>> print(d.tzinfo)
'UTC+00:00'
nordborn
fuente
1
AttributeError: 'module' object has no attribute 'timezone' Python 2.7.13 (predeterminado, 19 de enero de 2017, 14:48:08)
Marcin Owsiany
-6
from datetime import datetime 
from dateutil.relativedelta import relativedelta
d = datetime.now()
date = datetime.isoformat(d).split('.')[0]
d_month = datetime.today() + relativedelta(months=1)
next_month = datetime.isoformat(d_month).split('.')[0]
Mrudula Athuluri
fuente
-13

Las fechas UTC no necesitan ninguna información de zona horaria ya que son UTC, lo que por definición significa que no tienen compensación.

Ignacio Vazquez-Abrams
fuente
10
Por lo que puedo decir de docs.python.org/library/datetime.html , una fecha y hora sin tzinfo es aquella en la que la zona horaria no está especificada. Aquí se ha especificado la zona horaria , por lo que lógicamente debería estar presente. Hay una gran diferencia entre una fecha / hora sin una zona horaria asociada y una que definitivamente está en UTC. (Idealmente, deberían ser diferentes tipos de IMO, pero ese es otro asunto ...)
Jon Skeet
@ JonSkeet Creo que te estás perdiendo el punto de Ignacio de que UTC no es una zona horaria. Increíble que esta respuesta tiene -9 puntuación mientras escribo esto ...
CS
3
@CS: Bueno, Ignacio nunca dijo que ... y aunque estrictamente hablando, UTC no es una zona horaria, generalmente se trata como una para hacer la vida considerablemente más simple (incluso en Python, por ejemplo, con pytz.utc). Tenga en cuenta que hay una gran diferencia entre un valor cuyo desplazamiento de UTC es desconocido y uno donde se sabe que es 0. Esto último es lo que utcnow() debería devolver, IMO. Eso encajaría con "Un objeto consciente se utiliza para representar un momento específico en el tiempo que no está abierto a interpretación" según la documentación.
Jon Skeet