Come posso ottenere un valore di datetime.today () in Python che è "timezone aware"?


310

Sto cercando di sottrarre un valore di data dal valore di datetime.today()per calcolare quanto tempo fa era qualcosa. Ma si lamenta:

TypeError: can't subtract offset-naive and offset-aware datetimes

Il valore datetime.today()non sembra essere "a conoscenza del fuso orario", mentre l'altro valore della mia data lo è. Come posso ottenere un valore datetime.today()che sia consapevole del fuso orario?

In questo momento, mi sta dando l'ora in ora locale, che sembra essere PST, cioè UTC - 8 ore. Nel peggiore dei casi, c'è un modo in cui posso inserire manualmente un valore di fuso orario datetimenell'oggetto restituito datetime.today()e impostarlo su UTC-8?

Naturalmente, la soluzione ideale sarebbe quella di conoscere automaticamente il fuso orario.



10
Sembra che possiamo usare datetime.now().astimezone()da Python 3.6
johnchen902,

Risposte:


362

Nella libreria standard non esiste un modo multipiattaforma per creare fusi orari consapevoli senza creare la propria classe di fuso orario.

Su Windows, c'è win32timezone.utcnow(), ma fa parte di pywin32. Vorrei piuttosto suggerire di utilizzare la libreria pytz , che ha un database costantemente aggiornato della maggior parte dei fusi orari.

Lavorare con i fusi orari locali può essere molto complicato (vedere i collegamenti "Ulteriori letture" di seguito), quindi potresti preferire utilizzare UTC in tutta l'applicazione, in particolare per operazioni aritmetiche come il calcolo della differenza tra due punti temporali.

È possibile ottenere la data / ora corrente in questo modo:

import pytz
from datetime import datetime
datetime.utcnow().replace(tzinfo=pytz.utc)

Tieni presente che datetime.today()e datetime.now()restituisci l' ora locale , non l'ora UTC, quindi applicare .replace(tzinfo=pytz.utc)ad esse non sarebbe corretto.

Un altro bel modo per farlo è:

datetime.now(pytz.utc)

che è un po 'più corto e fa lo stesso.


Ulteriori letture / osservazioni sul perché preferire UTC in molti casi:


75
Che ne dici datetime.now(pytz.utc)invece di datetime.utcnow().replace(tzinfo = pytz.utc)?
eumiro,

5
now(utc)non ritorna oggi (a meno che non sia mezzanotte in UTC), restituisce l'ora corrente in UTC. Devi anche .replace(hour=0, minute=0, ...)avere l'inizio della giornata (come datetime.today())
jfs

1
I documenti dicono che today()restituisce l'ora corrente, non la mezzanotte. Se esiste un caso d'uso in cui è richiesta la mezzanotte, sì, la sostituzione deve essere eseguita di conseguenza. Poiché la domanda originale riguardava la differenza tra data e ora, non penso che sia necessaria la mezzanotte.
AndiDog,

1
@AndiDog: Il mio commento implica che ho pensato (erroneamente) che datetime.today()è combine(date.today(), time()). datetimeha entrambi .now()e .today()metodi che (come hai giustamente sottolineato) restituiscono (quasi) la stessa cosa. Non esiste un date.now()metodo datee gli datetimeoggetti non sono intercambiabili. L'uso di un oggetto datetime invece di un dateoggetto può produrre bug sottili; Non vedo alcun motivo per datetime.today()esistere se è quasi un duplicato di datetime.now().
jfs,

6
Aggiungendo a questa risposta, se ti capita di usare django, usa sempre timezone.now()invece di datetime.now()poiché utilizzerà UTC automaticamente se USE_TZ = True. timezonesi trova su django.utils.timezone, documentazione: docs.djangoproject.com/en/1.11/topics/i18n/timezones
Ryan

107

Ottieni l'ora corrente, in un fuso orario specifico:

import datetime
import pytz
my_date = datetime.datetime.now(pytz.timezone('US/Pacific'))

2
Vedere questo .
mercoledì

1
NON utilizzare l'ora localizzata ad eccezione dell'output. Molte cose vanno male quando si usa il datetime basato sul fuso orario: un semplice timedelta non tiene conto dell'ora legale a meno che non siate in ora UTC per cominciare. Usa sempre il fuso orario in base a UTC. convertire in fuso orario locale in uscita quando necessario.
MrE,

4
Per ribadire un disaccordo con @MrE che ho precedentemente espresso nei commenti sulla risposta accettata: ci sono ragioni perfettamente valide per lavorare con orari di dati localizzati e "NON si dovrebbe usare l'ora localizzata tranne che per l'output" è un consiglio eccessivamente ampio. Supponiamo di aggiungere 1 giorno a un datetime poche ore prima di un limite di ora legale in cui gli orologi tornano indietro di un'ora. Quale risultato vuoi? Se ritieni che l'ora dovrebbe essere la stessa, utilizza orari di dati localizzati. Se pensi che dovrebbe essere un'ora prima, utilizza UTC o i tempi di tempo ingenui. Il che ha senso dipende dal dominio.
Mark Amery,

@MarkAmery, per quanto posso essere d'accordo sul fatto che potresti voler AGGIUNGERE o SOTTOTARE un numero di giorni o ore e non preoccuparti dei problemi del fuso orario (come il tuo esempio), questo commento riguarda il passaggio del tempo corretto del fuso orario a un cliente. Poiché Python viene utilizzato principalmente per i processi di back-end, passa i tempi a un client. Il server dovrebbe sempre passare la data / ora in UTC e il client dovrebbe convertirlo nella propria data / ora / fuso orario locale, altrimenti accadono cose brutte: basta controllare l'output di datetime.datetime(2016, 11, 5, 9, 43, 45, tzinfo=pytz.timezone('US/Pacific'))e vedere se è quello che ti aspettavi
MrE

"Il server dovrebbe sempre passare la data / ora in UTC e il client dovrebbe convertirlo nella propria data / ora / fuso orario locale" - no, questo non è universalmente vero. A volte l'utilizzo del fuso orario del cliente è inappropriato e il fuso orario appropriato deve essere trasmesso come parte dei dati. Se, come londinese, vedo gli orari delle riunioni di un club di scacchi di San Francisco sul loro sito Web, dovrei vederli a San Francisco, non a Londra.
Mark Amery,

69

In Python 3, la libreria standard rende molto più semplice specificare UTC come fuso orario:

>>> import datetime
>>> datetime.datetime.now(datetime.timezone.utc)
datetime.datetime(2016, 8, 26, 14, 34, 34, 74823, tzinfo=datetime.timezone.utc)

Se si desidera una soluzione che utilizza solo la libreria standard e che funziona sia in Python 2 che in Python 3, vedere la risposta di jfs .

Se hai bisogno del fuso orario locale, non di UTC, vedi la risposta di Mihai Capotă


19

Ecco una soluzione stdlib che funziona su Python 2 e 3:

from datetime import datetime

now = datetime.now(utc) # Timezone-aware datetime.utcnow()
today = datetime(now.year, now.month, now.day, tzinfo=utc) # Midnight

dove todayè un'istanza datetime consapevole che rappresenta l'inizio del giorno (mezzanotte) in UTC ed utcè un oggetto tzinfo ( esempio dalla documentazione ):

from datetime import tzinfo, timedelta

ZERO = timedelta(0)

class UTC(tzinfo):
    def utcoffset(self, dt):
        return ZERO

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

    def dst(self, dt):
        return ZERO

utc = UTC()

Correlati: confronto delle prestazioni di diversi modi per ottenere la mezzanotte (inizio di una giornata) per un determinato orario UTC . Nota: è più complesso ottenere mezzanotte per un fuso orario con un offset UTC non fisso .


16

Un altro metodo per costruire un oggetto datetime sensibile al fuso orario che rappresenta l'ora corrente:

import datetime
import pytz

pytz.utc.localize( datetime.datetime.utcnow() )  

si noti che pytz.utce pytz.UTCsono entrambi definiti (e sono uguali)
drevicko,

2
Questa risposta è migliore di quella accettata poiché è più universale: replace()ing timezone è generalmente soggetto a errori nella maggior parte degli altri usi, mentre localize()ing è il modo preferito di assegnare il fuso orario a timestamp ingenui.
Antony Hatchkins,

@AntonyHatchkins: il .localize()metodo ha esito negativo per orari locali ambigui (input non utc). La risposta di @ philfreo che utilizza.now(pytz_timezone) continua a funzionare in questi casi.
jfs,

Come specificato nei documenti Python, .now(pytz_timezone)fa esattamente lo stesso di localize(utcnow)- prima genera l'ora corrente in UTC, quindi gli assegna un fuso orario: "<...> In questo caso il risultato è equivalente a tz.fromutc(datetime.utcnow().replace(tzinfo=tz))". Entrambe le risposte sono corrette e funzionano sempre.
Antony Hatchkins,

1
L'unico tempo ingenuo (non utc) che può essere reso sicuro in modo sicuro al fuso orario è ora : il sistema sottostante dovrebbe conoscere il valore UTC e pytzattraverso OLSON db dovrebbe sapere come convertirlo in qualsiasi fuso orario nel mondo. Rendere consapevole qualsiasi altro fuso orario ingenuo (non utc) è difficile a causa dell'ambiguità durante i turni di ora legale. Non è un problema di .localize(dargli un is_dstvalore lo fa funzionare per qualsiasi data). Questo è un problema inerente alla pratica dell'ora legale.
Antony Hatchkins,

16

Un one-liner che utilizza solo la libreria standard funziona a partire da Python 3.3. Puoi ottenere un datetimeoggetto sensibile al fuso orario locale usando astimezone(come suggerito da johnchen902 ):

from datetime import datetime, timezone

aware_local_now = datetime.now(timezone.utc).astimezone()

print(aware_local_now)
# 2020-03-03 09:51:38.570162+01:00

print(repr(aware_local_now))
# datetime.datetime(2020, 3, 3, 9, 51, 38, 570162, tzinfo=datetime.timezone(datetime.timedelta(0, 3600), 'CET'))

2
La documentazione è di grande aiuto, disponibile qui: docs.python.org/3.8/library/… . Questa funzionalità incredibilmente basilare è sepolta in profondità in un paragrafo oscuro nei documenti, in modo che questa risposta di StackOverflow sia effettivamente l'unico posto su tutta Internet con queste informazioni. Nella documentazione, si può anche vedere che, da Python 3.6, datetime.now()può essere chiamato senza argomenti e restituisce il risultato locale corretto ( datetimesi presume che gli ingenui siano nel fuso orario locale).
atimholt

8

Se stai usando Django , puoi impostare le date senza consapevolezza tz (solo UTC ).

Commenta la seguente riga in settings.py:

USE_TZ = True

8
dove hai visto django menzionato in questa domanda?
vonPetrushev,

1
Un'anima gentile ha cancellato le mie precedenti scuse di commento qui, quindi di nuovo: vergogna per me, risposta sbagliata poiché la domanda non è specifica di Django. L'ho lasciato perché potrebbe aiutare alcuni utenti comunque, ma lo eliminerò quando il punteggio si avvicina a 0. Se questa risposta è inappropriata, non esitare a votare.
Laffuste

6

pytz è una libreria Python che consente calcoli del fuso orario accurati e multipiattaforma utilizzando Python 2.3 o versioni successive.

Con lo stdlib, questo non è possibile.

Vedi una domanda simile su SO .


6

Ecco un modo per generarlo con lo stdlib:

import time
from datetime import datetime

FORMAT='%Y-%m-%dT%H:%M:%S%z'
date=datetime.strptime(time.strftime(FORMAT, time.localtime()),FORMAT)

data memorizzerà la data locale e l' offset da UTC , non la data nel fuso orario UTC, quindi è possibile utilizzare questa soluzione se è necessario identificare il fuso orario in cui viene generata la data . In questo esempio e nel mio fuso orario locale:

date
datetime.datetime(2017, 8, 1, 12, 15, 44, tzinfo=datetime.timezone(datetime.timedelta(0, 7200)))

date.tzname()
'UTC+02:00'

La chiave sta aggiungendo la %zdirettiva alla rappresentazione FORMAT, per indicare l'offset UTC della struttura temporale generata. Altri formati di rappresentazione possono essere consultati nei documenti del modulo datetime

Se è necessaria la data nel fuso orario UTC, è possibile sostituire time.localtime () con time.gmtime ()

date=datetime.strptime(time.strftime(FORMAT, time.gmtime()),FORMAT)

date    
datetime.datetime(2017, 8, 1, 10, 23, 51, tzinfo=datetime.timezone.utc)

date.tzname()
'UTC'

modificare

Funziona solo su python3 . La direttiva z non è disponibile sul codice _strptime.py di python 2


ValueError: 'z' è una direttiva errata nel formato '% Y-% m-% dT% H:% M:% S% z'
jno

Sei su Python 2, giusto? Sfortunatamente, sembra che la direttiva z non sia disponibile su Python 2. Codice
_strptime.py

6

Utilizzare dateutil come descritto in Python datetime.datetime.now () che è a conoscenza del fuso orario :

from dateutil.tz import tzlocal
# Get the current date/time with the timezone.
now = datetime.datetime.now(tzlocal())

1
Vedi questa risposta di JF Sebastian per una situazione in cui ciò dà un risultato errato.
Antony Hatchkins,

2
Penso che l'errore nell'altro post sia rilevante solo in casi d'uso specifici. La tzlocal()funzione è ancora una delle soluzioni più semplici e dovrebbe essere sicuramente menzionata qui.
user8162

2

Ottenere una data sensibile al utcfuso orario nel fuso orario è sufficiente per far funzionare la sottrazione della data.

Ma se vuoi una data in grado di riconoscere il fuso orario nel tuo fuso orario attuale, tzlocalè la strada da percorrere:

from tzlocal import get_localzone  # pip install tzlocal
from datetime import datetime
datetime.now(get_localzone())

PS dateutilha una funzione simile ( dateutil.tz.tzlocal). Ma nonostante la condivisione del nome, ha una base di codici completamente diversa, che come notato da JF Sebastian può dare risultati errati.


python è generalmente usato sul server. Il fuso orario locale su un server è in genere inutile e deve essere sempre impostato su UTC. L'impostazione di datetime tzinfo in questo modo non riesce in alcuni casi. meglio usare UTC, quindi localizzarlo nel fuso orario desiderato solo sull'output. qualsiasi calcolo di timedelta, ad esempio, non considera l'ora legale, quindi questi dovrebbero essere fatti in UTC, quindi localizzati.
MrE

@MrE Esempi sbagliati, offtopici?
Antony Hatchkins,

prova a utilizzare un oggetto datetime localizzato in un fuso orario che osserva l'ora legale, aggiungi un numero di giorni per cambiare lo stato dell'ora legale e noterai che l'operazione su oggetti datetime nel fuso orario localizzato non riesce e non rispetterà l'ora legale. Da qui il mio commento che dovresti SEMPRE fare qualsiasi operazione datetime in tempo UTC.
MrE,

il punto è: non farlo, esegui le tue operazioni in UTC, quindi usa datetime.astimezone (fuso orario) per convertire nella zona localtime in uscita.
MrE,

2

Ecco una soluzione che utilizza un fuso orario leggibile e che funziona con oggi ():

from pytz import timezone

datetime.now(timezone('Europe/Berlin'))
datetime.now(timezone('Europe/Berlin')).today()

Puoi elencare tutti i fusi orari come segue:

import pytz

pytz.all_timezones
pytz.common_timezones # or

1

Soprattutto per i fusi orari non UTC:

L'unico fuso orario che ha il suo metodo è timezone.utc, ma puoi fondere un fuso orario con qualsiasi offset UTC, se necessario, usando timedelta& timezonee forzandolo usando .replace.

In [1]: from datetime import datetime, timezone, timedelta

In [2]: def force_timezone(dt, utc_offset=0):
   ...:     return dt.replace(tzinfo=timezone(timedelta(hours=utc_offset)))
   ...:

In [3]: dt = datetime(2011,8,15,8,15,12,0)

In [4]: str(dt)
Out[4]: '2011-08-15 08:15:12'

In [5]: str(force_timezone(dt, -8))
Out[5]: '2011-08-15 08:15:12-08:00'

Usare timezone(timedelta(hours=n))come fuso orario è il vero proiettile d'argento qui, e ha molte altre utili applicazioni.


0

Se ottieni la data e l'ora correnti in Python, importa la data e l'ora, pacchetto pytz in Python dopo che otterrai la data e l'ora correnti come ..

from datetime import datetime
import pytz
import time
str(datetime.strftime(datetime.now(pytz.utc),"%Y-%m-%d %H:%M:%S%t"))

0

Un'altra alternativa, secondo me migliore, sta usando Penduluminvece di pytz. Considera il seguente codice semplice:

>>> import pendulum

>>> dt = pendulum.now().to_iso8601_string()
>>> print (dt)
2018-03-27T13:59:49+03:00
>>>

Per installare Pendulum e vedere la loro documentazione, vai qui . Ha tonnellate di opzioni (come il semplice ISO8601, RFC3339 e molti altri formati supportati), prestazioni migliori e tende a produrre codice più semplice.


non sono sicuro del motivo per cui il voto qui sotto, questo codice funziona in più programmi che eseguono 7/24 per me :). non che mi dispiaccia l'altra opinione, ma per favore dì perché non funziona per te, per consentirmi di controllarlo. Grazie in anticipo
ng10

0

Utilizzare il fuso orario come mostrato di seguito per una data e ora in grado di riconoscere il fuso orario. L'impostazione predefinita è UTC:

from django.utils import timezone
today = timezone.now()

0

Tyler di 'howchoo' ha realizzato un articolo davvero eccezionale che mi ha aiutato a farmi un'idea migliore degli oggetti Datetime, link sotto

Lavorare con Datetime

in sostanza, ho appena aggiunto quanto segue alla fine di entrambi i miei oggetti datetime

.replace(tzinfo=pytz.utc)

Esempio:

import pytz
import datetime from datetime

date = datetime.now().replace(tzinfo=pytz.utc)

0

prova pnp_datetime , tutto il tempo che è stato utilizzato e restituito è con fuso orario e non causerà problemi di offset e di rilevamento dell'offset.

>>> from pnp_datetime.pnp_datetime import Pnp_Datetime
>>>
>>> Pnp_Datetime.utcnow()
datetime.datetime(2020, 6, 5, 12, 26, 18, 958779, tzinfo=<UTC>)

0

Va sottolineato che a partire da Python 3.6, è necessaria solo la lib standard per ottenere un oggetto datetime sensibile al fuso orario che rappresenti l'ora locale (l'impostazione del sistema operativo). Utilizzo di astimezone ()

import datetime

datetime.datetime(2010, 12, 25, 10, 59).astimezone()
# e.g.
# datetime.datetime(2010, 12, 25, 10, 59, tzinfo=datetime.timezone(datetime.timedelta(seconds=3600), 'Mitteleuropäische Zeit'))

datetime.datetime(2010, 12, 25, 12, 59).astimezone().isoformat()
# e.g.
# '2010-12-25T12:59:00+01:00'

# I'm on CET/CEST

(vedi il commento di @ johnchen902).

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.