So machen Sie ein Datetime-Objekt bewusst (nicht naiv)

Lesezeit: 10 Minuten

Mark Tozzis Benutzeravatar
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?):

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

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

unutbus Benutzeravatar
unutbu

Um eine naive Datum/Uhrzeit-Zeitzone zu berücksichtigen, verwenden Sie im Allgemeinen die Lokalisierungsmethode:

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

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

Kangs Benutzeravatar
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:

from datetime import datetime, timezone

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

print(dt.isoformat())
# '2017-01-12T22:11:31+00:00'

Weniger Abhängigkeiten und keine Pytz-Probleme.

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

Sérgios Benutzeravatar
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

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

Benutzeravatar von xjcl
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)

Hängen Sie eine Zeitzone an:

>>> unaware.replace(tzinfo=ZoneInfo('Asia/Tokyo'))
datetime.datetime(2020, 10, 31, 12, 0, tzinfo=zoneinfo.ZoneInfo(key='Asia/Tokyo'))
>>> str(_)
'2020-10-31 12:00:00+09:00'

Hängen Sie die lokale Zeitzone des Systems an:

>>> unaware.replace(tzinfo=ZoneInfo('localtime'))
datetime.datetime(2020, 10, 31, 12, 0, tzinfo=zoneinfo.ZoneInfo(key='localtime'))
>>> str(_)
'2020-10-31 12:00:00+01:00'

Anschließend wird ordnungsgemäß auf andere Zeitzonen umgerechnet:

>>> unaware.replace(tzinfo=ZoneInfo('localtime')).astimezone(ZoneInfo('Asia/Tokyo'))
datetime.datetime(2020, 10, 31, 20, 0, tzinfo=backports.zoneinfo.ZoneInfo(key='Asia/Tokyo'))
>>> str(_)
'2020-10-31 20:00:00+09:00'

Wikipedia-Liste der verfügbaren Zeitzonen


Windows hat 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!

„localize“ ist die richtige Funktion. Beispiel:

localdatetime_aware = tz.localize(datetime_nonaware)

Oder ein vollständigeres Beispiel:

import pytz
from datetime import datetime
pytz.timezone('Australia/Melbourne').localize(datetime.now())

gibt mir einen timezonenbezogenen Datums-/Uhrzeitwert der aktuellen Ortszeit:

datetime.datetime(2017, 11, 3, 7, 44, 51, 908574, tzinfo=<DstTzInfo 'Australia/Melbourne' AEDT+11:00:00 DST>)

  • 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

Daniels Benutzeravatar
Daniel

Verwenden dateutil.tz.tzlocal() um die Zeitzone in Ihrer Nutzung zu erhalten datetime.datetime.now() Und datetime.datetime.astimezone():

from datetime import datetime
from dateutil import tz

unlocalisedDatetime = datetime.now()

localisedDatetime1 = datetime.now(tz = tz.tzlocal())
localisedDatetime2 = datetime(2017, 6, 24, 12, 24, 36, tz.tzlocal())
localisedDatetime3 = unlocalisedDatetime.astimezone(tz = tz.tzlocal())
localisedDatetime4 = unlocalisedDatetime.replace(tzinfo = tz.tzlocal())

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

1450310cookie-checkSo machen Sie ein Datetime-Objekt bewusst (nicht naiv)

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy