tra - time python 3




Come rendere consapevole un fuso orario datetime inconsapevole in python (6)

Cosa devo fare

Ho un oggetto datetime non consapevole del fuso orario, a cui ho bisogno di aggiungere un fuso orario per poterlo confrontare con altri oggetti datetime timezone-aware. Non voglio convertire la mia intera applicazione in timezone inconsapevole per questo caso legacy.

Quello che ho provato

Innanzitutto, per dimostrare il 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

Innanzitutto, ho provato 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
>>>

Non è terribilmente sorprendente questo fallimento, dal momento che sta effettivamente cercando di fare una conversione. Sostituire sembrava una scelta migliore (come per Python: come ottenere un valore di datetime.today () che è "timezone aware"? ):

>>> 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
>>> 

Ma come puoi vedere, la sostituzione sembra impostare tzinfo, ma non rendere l'oggetto consapevole. Mi sto apprestando a ricorrere alla correzione della stringa di input per avere un fuso orario prima di analizzarlo (sto usando dateutil per l'analisi, se questo è importante), ma ciò sembra incredibilmente clemente.

Inoltre, ho provato questo in entrambi Python 2.6 e Python 2.7, con gli stessi risultati.

Contesto

Sto scrivendo un parser per alcuni file di dati. C'è un vecchio formato che devo supportare dove la stringa della data non ha un indicatore del fuso orario. Ho già risolto l'origine dei dati, ma devo ancora supportare il formato dei dati legacy. Una conversione una tantum dei dati legacy non è un'opzione per varie ragioni BS aziendali. Mentre in generale, non mi piace l'idea di hard-coding di un fuso orario predefinito, in questo caso sembra l'opzione migliore. So con ragionevole certezza che tutti i dati legacy in questione sono in UTC, quindi sono pronto ad accettare il rischio di inadempienza in questo caso.


Ho usato da dt_aware a dt_unware

dt_unaware = dt_aware.replace(tzinfo=None)

e dt_unware su dt_aware

from pytz import timezone
localtz = timezone('Europe/Lisbon')
dt_aware = localtz.localize(dt_unware)

ma rispondere prima è anche una buona soluzione.


In generale, per creare un ingenuo datetime timezone-aware, utilizzare il metodo localize :

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

Per il fuso orario UTC, non è realmente necessario utilizzare la localize poiché non esiste un calcolo dell'ora legale da gestire:

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

lavori. ( .replace restituisce un nuovo datetime, non modifica unaware ).


Per aggiungere il fuso orario locale; (usando dateutil)

from dateutil import tz
import datetime

dt_unaware = datetime.datetime(2017, 6, 24, 12, 24, 36)

dt_aware = dt_unaware.replace(tzinfo=tz.tzlocal())

Questo codifica le answers @ Sérgio e @ unutbu. pytz.timezone "solo con un oggetto pytz.timezone o una stringa del fuso orario IANA .

def make_tz_aware(dt, tz='UTC', is_dst=None):
    """Add timezone information to a datetime object, only if it is naive."""
    tz = dt.tzinfo or tz
    try:
        tz = pytz.timezone(tz)
    except AttributeError:
        pass
    return tz.localize(dt, is_dst=is_dst) 

Questo sembra come dovrebbe fare datetime.localize() (o .inform() o .awarify() ), accettare sia stringhe che gli oggetti del fuso orario per l'argomento tz e impostare l'impostazione predefinita su UTC se non viene specificato alcun fuso orario.


Tutti questi esempi utilizzano un modulo esterno, ma puoi ottenere lo stesso risultato usando solo il modulo datetime, come anche presentato in questa risposta SO :

from datetime import datetime
from datetime import timezone

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

print(dt.replace(tzinfo=timezone.utc).isoformat())
'2017-01-12T22:11:31+00:00'

Meno dipendenze e nessun problema di pytz.

NOTA: se si desidera utilizzarlo con python3 e python2, è possibile utilizzarlo anche per l'importazione del fuso orario (hardcoded per 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()

Uso questa dichiarazione in Django per convertire un tempo inconsapevole in un consapevole:

from django.utils import timezone

dt_aware = timezone.make_aware(dt_unaware, timezone.get_current_timezone())




timezone