Perché datetime.datetime.utcnow () non contiene informazioni sul fuso orario?


285
datetime.datetime.utcnow()

Perché questo datetimenon ha alcuna informazione sul fuso orario dato che è esplicitamente un UTC datetime?

Mi aspetto che ciò contenga tzinfo.


Come convertire un normale campo data in formato iso di tipo stringa in formato utc?
Navi,

Risposte:


192

Ciò significa che il fuso orario è ingenuo, quindi non puoi usarlo datetime.astimezone

puoi dargli un fuso orario come questo

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

ora puoi cambiare i fusi orari

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

Per ottenere l'ora corrente in un determinato fuso orario, puoi passare datetime.now()direttamente a tzinfo :

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

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

Funziona per qualsiasi fuso orario, compresi quelli che osservano l'ora legale (DST), vale a dire, funziona per i fusi orari che possono avere offset utc diversi in momenti diversi (offset utc non fisso). Non utilizzare tz.localize(datetime.now()): potrebbe non riuscire durante la transizione di fine ora legale quando l'ora locale è ambigua.


216
Ma non c'è una buona ragione per essere ingenuo nel fuso orario - è specificato per essere UTC. Perché è necessario cercare una libreria di terze parti per farlo funzionare correttamente?
Mark Ransom,

4
Sono d'accordo; per me i tempi "ingenui" sono completamente inutili. Al momento si discute sulla lista di Python sull'aggiunta di pytz allo stdlib; il problema non è la licenza ma il fatto che i dati del fuso orario vengono aggiornati così spesso (cosa che Python stesso non può essere). Inoltre, pytz non implementa l'interfaccia tzinfo nel modo previsto, in modo da poter ricevere errori se si tenta di utilizzare alcuni dei fusi orari della città astimezone. Quindi datetime non ha solo fusi orari nativi, ma l'unica implementazione ampiamente disponibile di tzinfo non è conforme al presunto standard.
mercoledì

5
@bobince Perché pytz e le librerie datetime standard non funzionano per te? Il core Python e il pytz in evoluzione come progetti indipendenti riducono la complessità logistica per il team principale. Sì, ridurre la complessità per il core team di Python aumenta la complessità per tutti gli utenti di Python che hanno bisogno di gestire i fusi orari ma, credo, abbiano preso questa decisione per una buona ragione. La regola "La libreria standard non ha istanze di tzinfo ..." è ottima perché è semplice, perché fare un'eccezione qui?
Derek Litz,

15
Che ne dici diu=datetime.now(pytz.utc)
Craig McQueen del

4
@bain: non usare tz.localize(datetime.now()); usa datetime.now(tz)invece.
jfs,

142

Nota che per Python 3.2 in poi, il datetimemodulo contiene datetime.timezone. La documentazione per datetime.utcnow()dice:

Un datetime UTC corrente consapevole può essere ottenuto chiamando .datetime.now(timezone.utc)

Quindi puoi fare:

>>> import datetime
>>> datetime.datetime.now(datetime.timezone.utc)
datetime.datetime(2014, 7, 10, 2, 43, 55, 230107, tzinfo=datetime.timezone.utc)

2
Quale è preferito? datetime.now(timezone.utc)o datetime.utcnow(timezone.utc)?
Jesse Webb,

8
datetime.utcnow()non accetta argomenti. Quindi dovrebbe essere datetime.now(timezone.utc).
Craig McQueen,

1
datetime.now()restituirà l'ora della macchina ma datetime.utcnow()restituirà l'ora UTC effettiva.
Babu,

13
@Babu: datetime.utcnow()non è impostato tzinfoper indicare che è UTC. Ma datetime.now(datetime.timezone.utc)restituisce l'ora UTC con tzinfo set.
Craig McQueen,

@CraigMcQueen Quindi se passiamo un tzoggetto nel costruttore ora restituirà il tempo di quel fuso orario? Ok! Grazie per averlo segnalato.
Babu,

71

Le librerie standard di Python non includono alcuna classe tzinfo (ma vedi pep 431 ). Posso solo indovinare i motivi. Personalmente penso che sia stato un errore non includere una classe tzinfo per UTC, perché quella non è abbastanza controversa da avere un'implementazione standard.

Modifica: sebbene non ci sia implementazione nella libreria, ce n'è una come esempio nella tzinfodocumentazione .

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

Per usarlo, per ottenere l'ora corrente come oggetto datetime consapevole:

from datetime import datetime 

now = datetime.now(utc)

C'è datetime.timezone.utcin Python 3.2+:

from datetime import datetime, timezone 

now = datetime.now(timezone.utc)

8
Vai a capire perché questa classe non è stata fornita in primo luogo (e, soprattutto, utilizzata per gli datetimeoggetti creati da utcnow()) ...
André Caron

17
L'oggetto fuso orario timezone.utcè stato finalmente aggiunto a Python 3.2. Per compatibilità con le versioni precedenti, utcnow()restituisce comunque un oggetto tempo senza fuso orario, ma puoi ottenere ciò che desideri chiamando now(timezone.utc).
mhsmith,

4
@rgove, questo è il tipo di correzione dei torti che avrebbe dovuto essere un gioco equo per Python 3. Non avrebbero dovuto preoccuparsi della retrocompatibilità. C'è un altro esempio che ho letto negli ultimi giorni: il structmodulo farebbe conversioni automatiche da Unicode a bytestring e la decisione finale è stata quella di interrompere la compatibilità con le versioni precedenti di Python 3 per impedire che una decisione sbagliata potesse andare avanti.
Mark Ransom,

2
Sono sbalordito che la tzinfodocumentazione di Python includa esempi di codice per implementarlo, ma non includono quella funzionalità nello stesso datetime! docs.python.org/2/library/datetime.html#datetime.tzinfo.fromutc
LS

1
@LS sì, pytzè un'ottima risorsa. Quando avevo modificato la mia risposta per inserire il codice di esempio, qualcun altro l'aveva già suggerito e non volevo rubare il tuono.
Mark Ransom,

20

Il pytzmodulo è un'opzione e ce n'è un'altra python-dateutil, che sebbene sia anche un pacchetto di terze parti, potrebbe già essere disponibile a seconda delle altre dipendenze e del sistema operativo.

Volevo solo includere questa metodologia come riferimento: se hai già installato python-dateutilper altri scopi, puoi usarne tzinfoinvece di duplicarepytz

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

Tendo a concordare sul fatto che le chiamate a utcnowdovrebbero includere le informazioni sul fuso orario UTC. Ho il sospetto che questo non sia incluso perché la libreria di data e ora nativa è impostata su valori di tempo ingenui per compatibilità incrociata.


1
NameError: il nome 'dt' non è definito
xApple il

Stavo usando la chiamata datetime.datetime.utcfromtimestamp () e avevo bisogno di aggiungere tzinfo, la seconda soluzione ha funzionato per me: utcdt = datetime.datetime.utcfromtimestamp(1234567890).replace(dateutil.tz.tzutc())
Ian Lee

1
nota: a differenza datetime.now(pytz_tz)che funziona sempre; datetime.now(dateutil.tz.tzlocal())potrebbe non riuscire durante le transizioni di ora legale . PEP 495 - La disambiguazione dell'ora locale potrebbe migliorare la dateutilsituazione in futuro.
jfs,

@IanLee: è possibile utilizzare utc_dt = datetime.fromtimestamp(1234567890, dateutil.tz.tzutc())(nota: dateutilcon un offset utc non fisso (come dateutil.tz.tzlocal()) potrebbe non riuscire qui , utilizzare invece una pytzsoluzione basata su ).
jfs,

Dal momento che il mio programma era già importando dateutilper dateutil.parser, mi piaceva questa soluzione migliore. E 'stato così semplice come: utcCurrentTime = datetime.datetime.now(tz=dateutil.tz.tzutc()). Viola!!
LS

11

Julien Danjou ha scritto un buon articolo spiegando perché non dovresti mai avere a che fare con i fusi orari . Un estratto:

In effetti, l'API datetime Python restituisce sempre oggetti datetime inconsapevoli, il che è molto sfortunato. Infatti, non appena ottieni uno di questi oggetti, non c'è modo di sapere quale sia il fuso orario, quindi questi oggetti sono abbastanza "inutili" da soli.

Purtroppo, anche se puoi usarlo utcnow(), non vedrai ancora le informazioni sul fuso orario, come hai scoperto.

raccomandazioni:

  • Utilizzare sempre datetimeoggetti consapevoli , ad esempio con informazioni sul fuso orario. Ciò garantisce che sia possibile confrontarli direttamente (gli datetime oggetti consapevoli e inconsapevoli non sono confrontabili) e li restituirà correttamente agli utenti. Sfrutta pytz per avere oggetti fuso orario.

  • Utilizzare ISO 8601 come formato stringa di input e output. Utilizzare datetime.datetime.isoformat()per restituire i timestamp come stringa formattata utilizzando quel formato, che include le informazioni sul fuso orario.

  • Se è necessario analizzare stringhe contenenti timestamp formattati ISO 8601, è possibile fare affidamento su iso8601, che restituisce timestamp con le informazioni corrette sul fuso orario. Ciò rende i timestamp direttamente comparabili.


1
Questa è una raccomandazione leggermente fuorviante. La regola empirica è, non trattare mai i fusi orari. Conservare e trasmettere sempre tz oggetti utc inconsapevoli (oggetti d'epoca). Il fuso orario dovrebbe essere calcolato solo al momento della rappresentazione nell'interfaccia utente
nehem

1
Sembra che corrisponda già abbastanza bene ai pensieri di Julien. Quali delle sue raccomandazioni specifiche (come indicato sopra) sono fuorvianti?
Joe D'Andrea,

10

Per aggiungere timezoneinformazioni in Python 3.2+

import datetime

>>> d = datetime.datetime.now(tz=datetime.timezone.utc)
>>> print(d.tzinfo)
'UTC+00:00'

1
AttributeError: 'module' object has no attribute 'timezone' Python 2.7.13 (impostazione predefinita, 19 gennaio 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]

-13

Le date UTC non richiedono alcuna informazione sul fuso orario poiché sono UTC, il che per definizione significa che non hanno offset.


10
Per quanto ne so da docs.python.org/library/datetime.html , un datetime senza tzinfo è quello in cui il fuso orario non è specificato. Qui è stato specificato il fuso orario , quindi logicamente dovrebbe essere presente. C'è una grande differenza tra una data / ora senza un fuso orario associato e uno che è sicuramente in UTC. (Idealmente dovrebbero essere diversi tipi IMO, ma questa è un'altra questione ...)
Jon Skeet,

@JonSkeet Penso che ti stia perdendo il punto di Ignacio che UTC non sia un fuso orario. Incredibile che questa risposta abbia un punteggio di -9 mentre scrivo questo ...
CS

3
@CS: Beh, Ignacio non ha mai affermato che ... e mentre, a rigor di termini, UTC non è un fuso orario, di solito viene trattato come uno per rendere la vita considerevolmente più semplice (incluso in Python, ad esempio con pytz.utc). Si noti che esiste una grande differenza tra un valore il cui offset da UTC è sconosciuto e uno in cui è noto che è 0. Quest'ultimo è ciò che utcnow() dovrebbe restituire, IMO. Ciò si adatterebbe con "Un oggetto consapevole viene usato per rappresentare un momento specifico nel tempo che non è aperto all'interpretazione" come da documentazione.
Jon Skeet,
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.