So machen Sie ein Datetime-Objekt bewusst (nicht naiv)
Lesezeit: 10 Minuten
Mark Tozzi
Was ich tun muss
Ich habe ein datetime-Objekt, das keine Zeitzone kennt, dem ich eine Zeitzone hinzufügen muss, um es mit anderen datetime-Objekten, die die Zeitzone kennen, vergleichen zu können. Ich möchte für diesen einen Legacy-Fall nicht meine gesamte Anwendung ohne Kenntnis der Zeitzone umstellen.
Was ich versucht habe
Um zunächst das Problem zu veranschaulichen:
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
Zuerst habe ich astimezone ausprobiert:
>>> unaware.astimezone(pytz.UTC)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ValueError: astimezone() cannot be applied to a naive datetime
>>>
Es ist nicht besonders überraschend, dass dies fehlschlug, da es sich tatsächlich um eine Konvertierung handelt. Ersetzen schien eine bessere Wahl zu sein (gemäß Wie erhalte ich in Python einen Wert von datetime.today(), der „zeitzonenbewusst“ ist?):
Aber wie Sie sehen können, scheint replace die tzinfo festzulegen, das Objekt jedoch nicht darauf aufmerksam zu machen. Ich bereite mich darauf vor, die Eingabezeichenfolge erneut zu manipulieren, um vor dem Parsen eine Zeitzone zu erhalten (ich verwende dateutil zum Parsen, falls das wichtig ist), aber das erscheint mir unglaublich umständlich.
Außerdem habe ich dies sowohl in Python 2.6 als auch in Python 2.7 versucht, mit den gleichen Ergebnissen.
Kontext
Ich schreibe einen Parser für einige Datendateien. Es gibt ein altes Format, das ich unterstützen muss, bei dem die Datumszeichenfolge keine Zeitzonenanzeige hat. Ich habe die Datenquelle bereits korrigiert, muss aber weiterhin das alte Datenformat unterstützen. Eine einmalige Konvertierung der Altdaten ist aus verschiedenen betriebswirtschaftlichen Gründen keine Option. Obwohl mir die Idee, eine Standardzeitzone fest zu codieren, im Allgemeinen nicht gefällt, scheint es in diesem Fall die beste Option zu sein. Ich weiß mit hinreichender Sicherheit, dass alle fraglichen Altdaten in UTC vorliegen, daher bin ich bereit, das Risiko in Kauf zu nehmen, in diesem Fall standardmäßig darauf zurückzugreifen.
unaware.replace() würde zurückkehren None wenn es sich ändern würde unaware Objekt vorhanden. Das zeigt die REPL .replace() gibt ein neues zurück datetime Objekt hier.
– jfs
15. August 2011 um 16:40 Uhr
Was ich brauchte, als ich hierher kam: import datetime; datetime.datetime.now(datetime.timezone.utc)
– Martin Thoma
16. Januar 2018 um 9:32 Uhr
@MartinThoma Ich würde den Namen verwenden tz arg, um besser lesbar zu sein: datetime.datetime.now(tz=datetime.timezone.utc)
– Asklepios
14. Okt. 2019 um 14:45 Uhr
astimezone() kann jetzt (ab 3.6) für ein naives Objekt aufgerufen werden und sein Parameter kann (ab 3.3) weggelassen werden, sodass die Lösung so einfach ist wie unaware.astimezone()
– vashekcz
25. Februar 2021 um 21:49 Uhr
Ich habe den Trick gefunden: Europa/Paris, Berlin, MEZ, … sind in Pytz völlig fehlerhaft und ich war deswegen monatelang in einem Chaos der Instabilität. Ich habe das alles durch dateutil.tz.gettz(…) ersetzt und jetzt ist mein Code stabil und funktioniert! Mein Rat: Verzichten Sie komplett auf Pytz!
– Herve-Guerin
29. September 2022 um 10:35 Uhr
unutbu
Um eine naive Datum/Uhrzeit-Zeitzone zu berücksichtigen, verwenden Sie im Allgemeinen die Lokalisierungsmethode:
Für die UTC-Zeitzone ist die Verwendung nicht unbedingt erforderlich localize da es keine Sommerzeitberechnung gibt:
now_aware = unaware.replace(tzinfo=pytz.UTC)
funktioniert. (.replace gibt ein neues Datum/Uhrzeit zurück; es ändert sich nicht unaware.)
Nun, ich komme mir albern vor. Ersetzen gibt eine neue Datums- und Uhrzeitangabe zurück. Das steht auch direkt in den Dokumenten, und das habe ich völlig übersehen. Danke, genau das habe ich gesucht.
– Mark Tozzi
15. August 2011 um 14:24
„Ersetzen gibt ein neues Datum/Uhrzeit zurück.“ Ja. Der Hinweis, den Ihnen die REPL gibt, ist, dass sie Ihnen den zurückgegebenen Wert anzeigt. 🙂 🙂
– Karl Knechtel
15. August 2011 um 14:33
Wenn die Zeitzone nicht UTC ist, verwenden Sie den Konstruktor nicht direkt: aware = datetime(..., tz)verwenden .localize() stattdessen.
– jfs
8. März 2014 um 21:07 Uhr
Es ist erwähnenswert, dass die Ortszeit möglicherweise nicht eindeutig ist. tz.localize(..., is_dst=None) behauptet, dass dies nicht der Fall sei.
– jfs
8. März 2014 um 21:10 Uhr
Putz hat einen Fehler, der die Zeitzone von Amsterdam auf + 20 Minuten zur UTC setzt, eine veraltete Zeitzone von 1937. Sie hatten einen Job, Pytz.
– Boris
18. Februar 2020 um 16:09
kang
Alle diese Beispiele verwenden ein externes Modul, aber Sie können das gleiche Ergebnis erzielen, indem Sie nur das datetime-Modul verwenden, wie auch in dieser SO-Antwort dargestellt:
HINWEIS: Wenn Sie dies mit Python3 und Python2 verwenden möchten, können Sie dies auch für den Zeitzonenimport verwenden (fest codiert für 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()
Sehr gute Antwort zur Vorbeugung pytz Probleme, ich bin froh, dass ich etwas nach unten gescrollt habe! Wollte mich nicht damit auseinandersetzen pytz tatsächlich auf meinen Remote-Servern 🙂
– Tregoreg
1. Februar 2017 um 20:20 Uhr
Beachten Sie, dass from datetime import timezone Funktioniert in py3, aber nicht in py2.7.
– 7yl4r
20. Februar 2017 um 16:44
Das sollten Sie beachten dt.replace(tzinfo=timezone.utc) Gibt ein neues Datum/Uhrzeit zurück, es ändert sich nicht dt an Ort und Stelle. (Ich werde bearbeiten, um dies zu zeigen).
– Blairg23
28. März 2017 um 0:58
Wie können Sie, anstatt timezone.utc zu verwenden, eine andere Zeitzone als Zeichenfolge bereitstellen (z. B. „Amerika/Chicago“)?
– Trottel
26. September 2017 um 18:36 Uhr
@bumpkin besser spät als nie, denke ich: tz = pytz.timezone('America/Chicago')
– Florian
13. Februar 2019 um 11:40 Uhr
Sergio
Ich habe dieses Python-2-Skript im Jahr 2011 geschrieben, aber nie überprüft, ob es unter Python 3 funktioniert.
Ich war von dt_aware zu dt_unaware gewechselt:
dt_unaware = dt_aware.replace(tzinfo=None)
und dt_unware zu dt_aware:
from pytz import timezone
localtz = timezone('Europe/Lisbon')
dt_aware = localtz.localize(dt_unware)
Du könntest benutzen localtz.localize(dt_unware, is_dst=None) eine Ausnahme auslösen, wenn dt_unware stellt eine nicht vorhandene oder mehrdeutige Ortszeit dar (Hinweis: In der vorherigen Überarbeitung Ihrer Antwort gab es kein solches Problem localtz war UTC, da es in UTC keine Sommerzeitübergänge gibt
– jfs
15. Mai 2014 um 19:55 Uhr
@JF Sebastian, erster Kommentar angewendet
– Sergio
15. Mai 2014 um 20:59
Ich freue mich, dass Sie beide Richtungen der Konvertierung zeigen.
– Christian Lange
24. Juni 2016 um 1:08
@Sérgio und wenn Sie dieses Argument in .replace einfügen und „TypeError: replace() got an unerwartetes Schlüsselwortargument ‚tzinfo‘“ erhalten? Was kann man für dieses Problem tun?
– Gustavo Röttgering
22. Juni 2020 um 19:36 Uhr
Googol
Ich verwende diese Anweisung in Django, um eine unbewusste Zeit in eine bewusste umzuwandeln:
from django.utils import timezone
dt_aware = timezone.make_aware(dt_unaware, timezone.get_current_timezone())
xjcl
Python 3.9 fügt das hinzu zoneinfo Modul Jetzt wird also nur noch die Standardbibliothek benötigt!
from zoneinfo import ZoneInfo
from datetime import datetime
unaware = datetime(2020, 10, 31, 12)
Windowshat kein System-Zeitzonendatenbank, daher ist hier ein zusätzliches Paket erforderlich:
pip install tzdata
Es gibt einen Backport, der die Nutzung ermöglicht zoneinfo In Python 3.6 bis 3.8:
pip install backports.zoneinfo
Dann:
from backports.zoneinfo import ZoneInfo
Unter Windows müssen Sie dies auch tun pip install tzdata
– Fobersteiner
11. Juni 2020 um 17:19 Uhr
@MrFuppes Danke für den Tipp! Ich werde das morgen testen und es zu meiner Antwort machen. Wissen Sie, wie die Situation auf Macs ist?
– xjcl
11. Juni 2020 um 22:32 Uhr
@xjcl Du brauchst pip install tzdata auf jeder Plattform, auf der das Betriebssystem keine Zeitzonendatenbank bereitstellt. Macs sollten sofort funktionieren. Die Installation wird nicht schaden tzdata unbedingt (da die Systemdaten Vorrang haben). tzdata), wenn Ihre Anwendung Zeitzonendaten benötigt.
– Paul
21. September 2020 um 19:29 Uhr
@xjcl tzdata ist praktisch Teil der Standardbibliothek (wir nennen es ein „First-Party-Paket“), Sie müssen es nur per Pip installieren, da es einen viel schnelleren Veröffentlichungsrhythmus als CPython hat.
– Paul
27. September 2020 um 0:18
Ich wollte erwähnen, dass es sehr einfach ist, dies nur unter Windows bedingt zur Datei „requirements.txt“ hinzuzufügen: tzdata; sys_platform == "win32" (von: stackoverflow.com/a/35614580/705296)
– Joe Sadoski
31. Mai 2022 um 14:37 Uhr
Ich stimme den vorherigen Antworten zu und es ist in Ordnung, wenn Sie in UTC beginnen können. Aber ich denke, es ist auch ein häufiges Szenario für Menschen, mit einem TZ-bewussten Wert zu arbeiten, der einen hat datetime, das eine lokale Nicht-UTC-Zeitzone hat.
Wenn Sie nur den Namen nennen würden, würde man wahrscheinlich daraus schließen, dass replace() anwendbar ist und das richtige Objekt mit Datum/Uhrzeit-Erkennung erzeugt. Das ist nicht der Fall.
Das replace( tzinfo=… ) scheint in seinem Verhalten zufällig zu sein. Es ist daher nutzlos. Benutzen Sie dies nicht!
Unter Windows müssen Sie dies auch tun pip install tzdata
– Fobersteiner
11. Juni 2020 um 17:19 Uhr
@MrFuppes Danke für den Tipp! Ich werde das morgen testen und es zu meiner Antwort machen. Wissen Sie, wie die Situation auf Macs ist?
– xjcl
11. Juni 2020 um 22:32 Uhr
@xjcl Du brauchst pip install tzdata auf jeder Plattform, auf der das Betriebssystem keine Zeitzonendatenbank bereitstellt. Macs sollten sofort funktionieren. Die Installation wird nicht schaden tzdata unbedingt (da die Systemdaten Vorrang haben). tzdata), wenn Ihre Anwendung Zeitzonendaten benötigt.
– Paul
21. September 2020 um 19:29 Uhr
@xjcl tzdata ist praktisch Teil der Standardbibliothek (wir nennen es ein „First-Party-Paket“), Sie müssen es nur per Pip installieren, da es einen viel schnelleren Veröffentlichungsrhythmus als CPython hat.
– Paul
27. September 2020 um 0:18
Ich wollte erwähnen, dass es sehr einfach ist, dies nur unter Windows bedingt zur Datei „requirements.txt“ hinzuzufügen: tzdata; sys_platform == "win32" (von: stackoverflow.com/a/35614580/705296)
Beachten Sie, dass datetime.astimezone werde zuerst deine umwandeln datetime Objekt in UTC und dann in die Zeitzone verschieben, was dem Aufrufen gleichkommt datetime.replace mit den ursprünglichen Zeitzoneninformationen None.
Wenn Sie es UTC machen möchten: .replace(tzinfo=dateutil.tz.UTC)
– Martin Thoma
19. August 2019 um 6:08
Ein Import weniger und nur: .replace(tzinfo=datetime.timezone.utc)
– kubanczyk
9. April 2020 um 15:16 Uhr
14503100cookie-checkSo machen Sie ein Datetime-Objekt bewusst (nicht naiv)yes
unaware.replace()
würde zurückkehrenNone
wenn es sich ändern würdeunaware
Objekt vorhanden. Das zeigt die REPL.replace()
gibt ein neues zurückdatetime
Objekt hier.– jfs
15. August 2011 um 16:40 Uhr
Was ich brauchte, als ich hierher kam:
import datetime; datetime.datetime.now(datetime.timezone.utc)
– Martin Thoma
16. Januar 2018 um 9:32 Uhr
@MartinThoma Ich würde den Namen verwenden
tz
arg, um besser lesbar zu sein:datetime.datetime.now(tz=datetime.timezone.utc)
– Asklepios
14. Okt. 2019 um 14:45 Uhr
astimezone()
kann jetzt (ab 3.6) für ein naives Objekt aufgerufen werden und sein Parameter kann (ab 3.3) weggelassen werden, sodass die Lösung so einfach ist wieunaware.astimezone()
– vashekcz
25. Februar 2021 um 21:49 Uhr
Ich habe den Trick gefunden: Europa/Paris, Berlin, MEZ, … sind in Pytz völlig fehlerhaft und ich war deswegen monatelang in einem Chaos der Instabilität. Ich habe das alles durch dateutil.tz.gettz(…) ersetzt und jetzt ist mein Code stabil und funktioniert! Mein Rat: Verzichten Sie komplett auf Pytz!
– Herve-Guerin
29. September 2022 um 10:35 Uhr