Cómo hacer que un objeto de fecha y hora sea consciente (no ingenuo)

Resuelto Mark Tozzi asked hace 13 años • 16 respuestas

Lo que necesito hacer

Tengo un objeto de fecha y hora que no reconoce la zona horaria, al que necesito agregar una zona horaria para poder compararlo con otros objetos de fecha y hora que reconocen la zona horaria. No quiero convertir toda mi aplicación a zona horaria sin tener en cuenta para este caso heredado.

Lo que he probado

Primero, para demostrar el problema:

Python 2.6.1 (r261:67515, Jun 24 2010, 21:47:49) 
[GCC 4.2.1 (Apple Inc. build 5646)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import datetime
>>> import pytz
>>> unaware = datetime.datetime(2011,8,15,8,15,12,0)
>>> unaware
datetime.datetime(2011, 8, 15, 8, 15, 12)
>>> aware = datetime.datetime(2011,8,15,8,15,12,0,pytz.UTC)
>>> aware
datetime.datetime(2011, 8, 15, 8, 15, 12, tzinfo=<UTC>)
>>> aware == unaware
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: can't compare offset-naive and offset-aware datetimes

Primero, probé astimezone:

>>> unaware.astimezone(pytz.UTC)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: astimezone() cannot be applied to a naive datetime
>>>

No es muy sorprendente que esto haya fallado, ya que en realidad está intentando realizar una conversión. Reemplazar parecía una mejor opción (según ¿ Cómo obtengo un valor de datetime.today() en Python que sea "consciente de la zona horaria"? ):

>>> unaware.replace(tzinfo=pytz.UTC)
datetime.datetime(2011, 8, 15, 8, 15, 12, tzinfo=<UTC>)
>>> unaware == aware
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: can't compare offset-naive and offset-aware datetimes
>>> 

Pero como puede ver, reemplazar parece configurar tzinfo, pero no hace que el objeto sea consciente. Me estoy preparando para volver a modificar la cadena de entrada para tener una zona horaria antes de analizarla (estoy usando dateutil para analizar, si eso importa), pero eso parece increíblemente torpe.

Además, probé esto tanto en Python 2.6 como en Python 2.7, con los mismos resultados.

Contexto

Estoy escribiendo un analizador para algunos archivos de datos. Hay un formato antiguo que necesito admitir donde la cadena de fecha no tiene un indicador de zona horaria. Ya arreglé la fuente de datos, pero aún necesito admitir el formato de datos heredado. Una conversión única de los datos heredados no es una opción por diversos motivos empresariales. Si bien en general no me gusta la idea de codificar una zona horaria predeterminada, en este caso parece la mejor opción. Sé con razonable confianza que todos los datos heredados en cuestión están en UTC, por lo que estoy preparado para aceptar el riesgo de utilizar eso de forma predeterminada en este caso.

Mark Tozzi avatar Aug 15 '11 19:08 Mark Tozzi
Aceptado

En general, para que una fecha y hora ingenua tenga en cuenta la zona horaria, utilice el método de localización :

import datetime
import pytz

unaware = datetime.datetime(2011, 8, 15, 8, 15, 12, 0)
aware = datetime.datetime(2011, 8, 15, 8, 15, 12, 0, pytz.UTC)

now_aware = pytz.utc.localize(unaware)
assert aware == now_aware

Para la zona horaria UTC, no es realmente necesario usarlo localizeya que no hay que manejar ningún cálculo de horario de verano:

now_aware = unaware.replace(tzinfo=pytz.UTC)

obras. ( .replacedevuelve una nueva fecha y hora; no se modifica unaware).

unutbu avatar Aug 15 '2011 13:08 unutbu

Todos estos ejemplos usan un módulo externo, pero puede lograr el mismo resultado usando solo el módulo de fecha y hora, como también se presenta en esta respuesta SO :

from datetime import datetime, timezone

dt = datetime.now()
dt = dt.replace(tzinfo=timezone.utc)

print(dt.isoformat())
# '2017-01-12T22:11:31+00:00'

Menos dependencias y sin problemas de pytz.

NOTA: Si desea utilizar esto con python3 y python2, también puede utilizarlo para la importación de zona horaria (codificada para UTC):

try:
    from datetime import timezone
    utc = timezone.utc
except ImportError:
    #Hi there python2 user
    class UTC(tzinfo):
        def utcoffset(self, dt):
            return timedelta(0)
        def tzname(self, dt):
            return "UTC"
        def dst(self, dt):
            return timedelta(0)
    utc = UTC()
kang avatar Jan 12 '2017 22:01 kang

Escribí este script de Python 2 en 2011, pero nunca verifiqué si funciona en Python 3.

Me mudé de dt_aware a dt_unaware:

dt_unaware = dt_aware.replace(tzinfo=None)

y dt_unware a dt_aware:

from pytz import timezone
localtz = timezone('Europe/Lisbon')
dt_aware = localtz.localize(dt_unware)
Sérgio avatar Nov 22 '2011 00:11 Sérgio