Wie erhalte ich in Python einen Wert für datetime.today (), der "zeitzonenabhängig" ist?

310

Ich versuche, einen Datumswert vom Wert von zu subtrahieren, um datetime.today()zu berechnen, wie lange etwas her ist. Aber es beschwert sich:

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

Der Wert datetime.today()scheint nicht "zeitzonenbewusst" zu sein, während mein anderer Datumswert ist. Wie erhalte ich einen datetime.today()zeitzonenabhängigen Wert ?

Im Moment gibt es mir die Zeit in der Ortszeit, die zufällig PST ist, dh UTC - 8 Stunden. Gibt es im schlimmsten Fall eine Möglichkeit, einen Zeitzonenwert manuell in das von zurückgegebene datetimeObjekt einzugeben datetime.today()und auf UTC-8 zu setzen?

Die ideale Lösung wäre natürlich, die Zeitzone automatisch zu kennen.

Mindthief
quelle
10
Scheint, als könnten wir datetime.now().astimezone()seit Python 3.6
johnchen902

Antworten:

362

In der Standardbibliothek gibt es keine plattformübergreifende Möglichkeit, bewusste Zeitzonen zu erstellen, ohne eine eigene Zeitzonenklasse zu erstellen.

Unter Windows gibt es win32timezone.utcnow(), aber das ist Teil von pywin32. Ich würde eher vorschlagen, die Pytz-Bibliothek zu verwenden , die eine ständig aktualisierte Datenbank der meisten Zeitzonen enthält.

Das Arbeiten mit lokalen Zeitzonen kann sehr schwierig sein (siehe Links "Weiterführende Literatur" weiter unten). Daher möchten Sie möglicherweise lieber UTC in Ihrer gesamten Anwendung verwenden, insbesondere für arithmetische Operationen wie die Berechnung der Differenz zwischen zwei Zeitpunkten.

Sie können das aktuelle Datum / die aktuelle Uhrzeit wie folgt abrufen:

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

Beachten Sie dies datetime.today()und datetime.now()geben Sie die Ortszeit und nicht die UTC-Zeit zurück, sodass eine Bewerbung .replace(tzinfo=pytz.utc)nicht korrekt wäre.

Ein weiterer guter Weg, dies zu tun, ist:

datetime.now(pytz.utc)

Das ist etwas kürzer und macht das gleiche.


Lesen Sie weiter / beobachten Sie, warum Sie UTC in vielen Fällen bevorzugen:

AndiDog
quelle
75
Wie wäre es datetime.now(pytz.utc)statt datetime.utcnow().replace(tzinfo = pytz.utc)?
Eumiro
5
now(utc)kehrt heute nicht zurück (es sei denn, es ist Mitternacht in UTC), sondern gibt die aktuelle Zeit in UTC zurück. Sie müssen auch .replace(hour=0, minute=0, ...)den Anfang des Tages bekommen (wie datetime.today())
jfs
1
Die Dokumente sagen, dass today()die aktuelle Zeit zurückgegeben wird, nicht Mitternacht. Wenn es einen Anwendungsfall gibt, in dem Mitternacht erforderlich ist, muss der Austausch entsprechend erfolgen. Da die ursprüngliche Frage den Unterschied zwischen Datum und Uhrzeit betraf, glaube ich nicht, dass Mitternacht erforderlich ist.
AndiDog
1
@AndiDog: Mein Kommentar impliziert , dass ich dachte , (falsch) , das datetime.today()ist combine(date.today(), time()). datetimehat beides .now()und .today()Methoden, die (wie Sie richtig betont haben) (fast) dasselbe zurückgeben. Es gibt keine date.now()Methode. dateund datetimeObjekte sind nicht austauschbar. Die Verwendung eines datetime-Objekts anstelle eines dateObjekts kann zu subtilen Fehlern führen. Ich sehe keinen Grund datetime.today()zu existieren, wenn es ein nahezu Duplikat von ist datetime.now().
JFS
6
Wenn Sie zu dieser Antwort hinzufügen, verwenden Sie immer Django timezone.now()anstelle von, datetime.now()da UTC automatisch verwendet wird, wenn USE_TZ = True. timezonebefindet sich unter django.utils.timezone, Dokumentation: docs.djangoproject.com/de/1.11/topics/i18n/timezones
Ryan
107

Holen Sie sich die aktuelle Zeit in einer bestimmten Zeitzone:

import datetime
import pytz
my_date = datetime.datetime.now(pytz.timezone('US/Pacific'))
Philfreo
quelle
2
Sehen Sie das .
wim
1
Sie sollten KEINE lokalisierte Zeit außer für die Ausgabe verwenden. Bei der Verwendung der zeitzonenbasierten Datums- und Uhrzeitangabe gehen viele Dinge schief: Bei einem einfachen Zeitdelta wird die Sommerzeit nur berücksichtigt, wenn Sie sich in der UTC-Zeit befinden. Verwenden Sie immer eine Zeitzone basierend auf UTC. Bei Bedarf in die lokale Zeitzone konvertieren.
MrE
4
Um eine Meinungsverschiedenheit mit @MrE zu wiederholen, die ich zuvor in den Kommentaren zur akzeptierten Antwort geäußert habe: Es gibt durchaus triftige Gründe, mit lokalisierten Datenzeiten zu arbeiten, und "Sie sollten keine lokalisierte Zeit außer für die Ausgabe verwenden" ist ein zu umfassender Rat. Angenommen, Sie fügen ein paar Stunden vor einer Sommerzeitgrenze, an der die Uhren um eine Stunde zurückgehen, einen Tag zu einer Datumszeit hinzu. Welches Ergebnis möchten Sie? Wenn Sie der Meinung sind, dass die Zeit gleich sein sollte, verwenden Sie lokalisierte Datumsangaben. Wenn Sie der Meinung sind, dass es eine Stunde früher sein sollte, verwenden Sie UTC- oder zeitzonennaive Datumsangaben. Was Sinn macht, ist domänenabhängig.
Mark Amery
@MarkAmery Soweit ich zustimmen kann, dass Sie möglicherweise eine Anzahl von Tagen oder Stunden HINZUFÜGEN oder SUBTRAKTIEREN möchten und sich nicht um Zeitzonenprobleme kümmern (wie in Ihrem Beispiel), bezieht sich dieser Kommentar auf das Übergeben einer zeitzonenbereinigten Zeit an einen Kunden. Da Python hauptsächlich für Back-End-Prozesse verwendet wird, werden die Zeiten an einen Client übergeben. Der Server sollte immer Datum / Uhrzeit in UTC übergeben und der Client sollte es in sein eigenes lokales Datum / Uhrzeit / Zeitzone konvertieren, da sonst schlimme Dinge passieren: Überprüfen Sie einfach die Ausgabe von datetime.datetime(2016, 11, 5, 9, 43, 45, tzinfo=pytz.timezone('US/Pacific'))und prüfen Sie, ob dies das ist, was Sie erwartet haben
MrE
"Der Server sollte Datum / Uhrzeit immer in UTC übergeben und der Client sollte es in sein eigenes lokales Datum / Uhrzeit / Zeitzone konvertieren" - nein, dies ist nicht allgemein gültig. Manchmal ist die Verwendung der Zeitzone des Kunden unangemessen und die entsprechende Zeitzone muss als Teil der Daten übertragen werden. Wenn ich als Londoner die Besprechungszeiten eines Schachclubs in San Francisco auf ihrer Website sehe, sollte ich sie in der Zeit von San Francisco sehen, nicht in der Zeit von London.
Mark Amery
69

In Python 3 erleichtert die Standardbibliothek die Angabe von UTC als Zeitzone erheblich:

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

Wenn Sie eine Lösung suchen, die nur die Standardbibliothek verwendet und sowohl in Python 2 als auch in Python 3 funktioniert, lesen Sie die Antwort von jfs .

Wenn Sie die lokale Zeitzone und nicht UTC benötigen, lesen Sie die Antwort von Mihai Capotă

Flimm
quelle
19

Hier ist eine stdlib-Lösung, die sowohl auf Python 2 als auch auf Python 3 funktioniert:

from datetime import datetime

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

Dabei todayhandelt es sich um eine bekannte datetime-Instanz, die den Beginn des Tages (Mitternacht) in UTC darstellt, und utcum ein tzinfo-Objekt ( Beispiel aus der Dokumentation ):

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

Verwandte Themen: Leistungsvergleich verschiedener Möglichkeiten, um Mitternacht (Beginn eines Tages) für eine bestimmte UTC-Zeit zu erhalten . Hinweis: Es ist komplexer, Mitternacht für eine Zeitzone mit einem nicht festen UTC-Versatz zu erhalten .

jfs
quelle
16

Eine andere Methode zum Erstellen eines zeitzonensensitiven datetime-Objekts, das die aktuelle Zeit darstellt:

import datetime
import pytz

pytz.utc.localize( datetime.datetime.utcnow() )  
Dariusz Walczak
quelle
Beachten Sie, dass pytz.utcund pytz.UTCbeide definiert sind (und gleich sind)
drevicko
2
Diese Antwort ist besser als die akzeptierte, da sie universeller ist: Die replace()Zeitzone ist in den meisten anderen Anwendungen im Allgemeinen fehleranfällig, während localize()ing die bevorzugte Methode ist, naiven Zeitstempeln Zeitzone zuzuweisen.
Antony Hatchkins
@AntonyHatchkins: .localize()Methode schlägt für mehrdeutige Ortszeiten fehl (Nicht-Utc-Eingabe). Die Antwort von @ philfreo, die verwendet.now(pytz_timezone) , funktioniert in solchen Fällen weiterhin.
JFS
Wie in Python-Dokumenten angegeben, .now(pytz_timezone)funktioniert genau das Gleiche wie localize(utcnow): Zuerst wird die aktuelle Zeit in UTC generiert, dann wird ihr eine Zeitzone zugewiesen: "<...> In diesem Fall entspricht das Ergebnis tz.fromutc(datetime.utcnow().replace(tzinfo=tz))". Beide Antworten sind richtig und funktionieren immer.
Antony Hatchkins
1
Die einzige naive (nicht-utc) Zeit, die sicher auf die Zeitzone aufmerksam gemacht werden kann, ist jetzt : Das zugrunde liegende System soll den UTC-Wert kennen und pytzüber OLSON db soll es wissen, wie es in eine beliebige Zeitzone auf der Welt konvertiert werden kann. Es ist schwierig, andere naive (nicht-utc) Zeitzonen bewusst zu machen, da die Sommerzeit nicht eindeutig ist. Das ist kein Problem .localize(wenn Sie ihm einen is_dstWert geben, funktioniert er für jedes Datum). Das ist ein inhärentes Problem der Sommerzeit.
Antony Hatchkins
16

Ein Einzeiler, der nur die Standardbibliothek verwendet, funktioniert ab Python 3.3. Sie können ein lokales zeitzonenbewusstes datetimeObjekt mit astimezone(wie von johnchen902 vorgeschlagen ) abrufen :

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'))
Mihai Capotă
quelle
2
Die Dokumentation ist eine große Hilfe, die Sie hier finden: docs.python.org/3.8/library/… . Diese unglaublich grundlegende Funktionalität ist tief in einem dunklen Absatz in den Dokumenten vergraben, so dass diese Stackoverflow-Antwort praktisch der einzige Ort im gesamten Internet mit diesen Informationen ist. In der Dokumentation ist auch zu sehen, dass Python 3.6 seit datetime.now()Argument 3.6 ohne Argumente aufgerufen werden kann und das richtige lokale Ergebnis zurückgibt ( datetimees wird angenommen, dass sich naive s in der lokalen Zeitzone befinden).
Atimholt
8

Wenn Sie Django verwenden , können Sie Daten festlegen, die nicht tz-fähig sind (nur UTC ).

Kommentieren Sie die folgende Zeile in settings.py:

USE_TZ = True
laffuste
quelle
8
Wo hast du Django in dieser Frage erwähnt?
vonPetrushev
1
Eine freundliche Seele hat meine vorherige Kommentar-Entschuldigung hier gelöscht, also noch einmal: Schande über mich, falsche Antwort, da die Frage nicht Django-spezifisch ist. Ich habe es verlassen, weil es einigen Benutzern sowieso helfen könnte, aber ich werde es löschen, wenn sich die Punktzahl 0 nähert. Wenn diese Antwort unangemessen ist, können Sie sie gerne ablehnen.
Laffuste
6

pytz ist eine Python-Bibliothek, die genaue und plattformübergreifende Zeitzonenberechnungen mit Python 2.3 oder höher ermöglicht.

Mit der stdlib ist dies nicht möglich.

Siehe eine ähnliche Frage zu SO .

user225312
quelle
6

Hier ist eine Möglichkeit, es mit der stdlib zu generieren:

import time
from datetime import datetime

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

Datum speichert das lokale Datum und den Versatz von UTC , nicht das Datum in der UTC-Zeitzone. Sie können diese Lösung also verwenden, wenn Sie ermitteln müssen, in welcher Zeitzone das Datum generiert wird . In diesem Beispiel und in meiner lokalen Zeitzone:

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

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

Der Schlüssel fügt der %zAnweisung FORMAT die Direktive hinzu, um den UTC-Offset der generierten Zeitstruktur anzugeben. Andere Darstellungsformate können in dem Datetime - Modul zu Rate gezogen werden docs

Wenn Sie das Datum in der UTC-Zeitzone benötigen, können Sie time.localtime () durch time.gmtime () ersetzen.

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'

Bearbeiten

Dies funktioniert nur unter Python3 . Die z-Direktive ist für Python 2 _strptime.py-Code nicht verfügbar

jcazor
quelle
ValueError: 'z' ist eine schlechte Anweisung im Format '% Y-% m-% dT% H:% M:% S% z'
jno
Du bist auf Python 2, richtig? Leider scheint die z-Direktive auf Python 2 nicht verfügbar zu sein. _Strptime.py code
jcazor
6

Verwenden Sie dateutil wie in Python datetime.datetime.now () beschrieben, das zeitzonenabhängig ist :

from dateutil.tz import tzlocal
# Get the current date/time with the timezone.
now = datetime.datetime.now(tzlocal())
G. Führ
quelle
1
In dieser Antwort von JF Sebastian finden Sie eine Situation, in der dies zu einem falschen Ergebnis führt.
Antony Hatchkins
2
Ich denke, der Fehler im anderen Beitrag ist nur in bestimmten Anwendungsfällen relevant. Die tzlocal()Funktion ist immer noch eine der einfachsten Lösungen und sollte hier unbedingt erwähnt werden.
user8162
2

Das Abrufen eines zeitzonensensitiven Datums in der utcZeitzone reicht aus, damit die Datumssubtraktion funktioniert.

Wenn Sie jedoch ein zeitzonenbewusstes Datum in Ihrer aktuellen Zeitzone wünschen, sollten Sie Folgendes tun tzlocal:

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

PS dateutilhat eine ähnliche Funktion ( dateutil.tz.tzlocal). Aber trotz der Namen des Teilens hat es eine ganz andere Codebasis, die als festgestellt , von JF Sebastian kann falsche Ergebnisse liefern.

Antony Hatchkins
quelle
Python wird normalerweise auf dem Server verwendet. Die lokale Zeitzone auf einem Server ist normalerweise sinnlos und sollte immer auf UTC eingestellt werden. Das Einstellen von datetime tzinfo auf diese Weise schlägt in einigen Fällen fehl. Verwenden Sie besser UTC und lokalisieren Sie dann nur bei der Ausgabe in der gewünschten Zeitzone. Bei einer Timedelta-Berechnung wird beispielsweise die Sommerzeit nicht berücksichtigt. Diese sollten daher in UTC durchgeführt und dann lokalisiert werden.
MrE
@ MrE Falsch, offtopic, Beispiele?
Antony Hatchkins
Versuchen Sie, ein Datums- / Uhrzeitobjekt zu verwenden, das in einer Zeitzone lokalisiert ist, in der die Sommerzeit beobachtet wird. Fügen Sie eine Anzahl von Tagen hinzu, um den Sommerzeitstatus zu ändern. Sie werden feststellen, dass die Bearbeitung von Datums- / Uhrzeitobjekten in der lokalisierten Zeitzone fehlschlägt und die Sommerzeit nicht berücksichtigt. Daher mein Kommentar, dass Sie IMMER eine Datums- / Uhrzeitoperation in UTC-Zeit ausführen sollten.
MrE
Der Punkt ist: Tun Sie dies nicht, führen Sie Ihre Operationen in UTC aus und verwenden Sie dann datetime.astimezone (Zeitzone), um bei der Ausgabe in die Ortszeitzone zu konvertieren.
MrE
2

Hier ist eine Lösung mit einer lesbaren Zeitzone, die mit today () funktioniert:

from pytz import timezone

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

Sie können alle Zeitzonen wie folgt auflisten:

import pytz

pytz.all_timezones
pytz.common_timezones # or
John Andrews
quelle
1

Speziell für Nicht-UTC-Zeitzonen:

Die einzige Zeitzone, die über eine eigene Methode verfügt, ist timezone.utc, aber Sie können eine Zeitzone bei Bedarf mit einem beliebigen UTC-Offset fummeln, indem Sie timedelta& verwenden timezoneund sie mit erzwingen .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'

Die Verwendung timezone(timedelta(hours=n))als Zeitzone ist hier die wahre Silberkugel, und es gibt viele andere nützliche Anwendungen.

tmck-code
quelle
0

Wenn Sie die aktuelle Uhrzeit und das aktuelle Datum in Python erhalten, importieren Sie Datum und Uhrzeit. Das Pytz-Paket in Python wird angezeigt, nachdem Sie das aktuelle Datum und die aktuelle Uhrzeit wie folgt erhalten haben.

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

Eine andere Alternative, meiner Meinung nach eine bessere, ist die Verwendung von Pendulumanstelle von pytz. Betrachten Sie den folgenden einfachen Code:

>>> import pendulum

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

Um Pendel zu installieren und deren Dokumentation zu sehen, klicken Sie hier . Es bietet unzählige Optionen (wie einfache ISO8601-, RFC3339- und viele andere Formatunterstützung), eine bessere Leistung und tendenziell einfacheren Code.

ng10
quelle
Ich bin mir nicht sicher, warum die Abstimmung hier unten stattfindet. Dieser Code funktioniert in mehreren Programmen, die für mich 24/24 ausführen :). nicht, dass mir eine andere Meinung etwas ausmacht, aber bitte sagen Sie, warum es bei Ihnen nicht funktioniert, damit ich es überprüfen kann. Vielen Dank im Voraus
ng10
0

Verwenden Sie die unten gezeigte Zeitzone für eine zeitzonenbezogene Datums- und Uhrzeitangabe. Der Standardwert ist UTC:

from django.utils import timezone
today = timezone.now()
Anupama V Iyengar
quelle
0

Tyler von 'howchoo' hat einen wirklich großartigen Artikel geschrieben, der mir geholfen hat, eine bessere Vorstellung von den Datetime-Objekten zu bekommen, Link unten

Arbeiten mit Datetime

Im Wesentlichen habe ich am Ende meiner beiden datetime-Objekte Folgendes hinzugefügt

.replace(tzinfo=pytz.utc)

Beispiel:

import pytz
import datetime from datetime

date = datetime.now().replace(tzinfo=pytz.utc)
Jose
quelle
0

Versuchen Sie es mit pnp_datetime . Die gesamte Zeit, die verwendet und zurückgegeben wurde, ist mit der Zeitzone verbunden und verursacht keine Offset-naiven und Offset-fähigen Probleme.

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

Es sollte betont werden, dass Sie seit Python 3.6 nur die Standardbibliothek benötigen, um ein zeitzonenbewusstes datetime-Objekt zu erhalten, das die Ortszeit darstellt (die Einstellung Ihres Betriebssystems). Verwenden von 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

(siehe Kommentar von @ johnchen902).

MrFuppes
quelle