Wie mache ich ein Datetime-Objekt in Python bewusst (nicht naiv)?

Lesezeit: 10 Minuten

Benutzeravatar von Mark Tozzi
Markus Tozi

Was ich tun muss

Ich habe ein zeitzonenunabhängiges datetime-Objekt, dem ich eine Zeitzone hinzufügen muss, um es mit anderen zeitzonenabhängigen datetime-Objekten vergleichen zu können. Ich möchte nicht meine gesamte Anwendung für diesen einen Legacy-Fall ohne Kenntnis der Zeitzone konvertieren.

Was ich versucht habe

Erstmal um das Problem zu demonstrieren:

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 sehr überraschend, dass dies fehlgeschlagen ist, da es tatsächlich versucht, eine Konvertierung durchzuführen. Ersetzen schien eine bessere Wahl zu sein (gemäß Wie bekomme ich einen Wert von datetime.today() in Python, 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 zu setzen, aber das Objekt nicht darauf aufmerksam zu machen. Ich bereite mich darauf vor, die Eingabezeichenfolge zu bearbeiten, um eine Zeitzone zu erhalten, bevor ich sie parse (ich verwende dateutil zum Parsing, wenn das wichtig ist), aber das scheint unglaublich klobig zu sein.

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 repariert, aber ich muss noch das Legacy-Datenformat unterstützen. Eine einmalige Konvertierung der Altdaten ist aus verschiedenen betriebswirtschaftlichen Gründen nicht möglich. Während ich im Allgemeinen die Idee nicht mag, eine Standardzeitzone fest zu codieren, scheint es in diesem Fall die beste Option zu sein. Ich weiß mit angemessener Sicherheit, dass alle fraglichen Altdaten in UTC vorliegen, daher bin ich bereit, das Risiko eines Verzugs in diesem Fall in Kauf zu nehmen.

  • 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. Oktober 2019 um 2:45 Uhr

  • astimezone() kann jetzt (ab 3.6) auf einem naiven Objekt aufgerufen werden, und sein Parameter kann (ab 3.3) weggelassen werden, sodass die Lösung so einfach ist wie unaware.astimezone()

    – waschekcz

    25. Februar 2021 um 21:49 Uhr


  • Ich habe den Trick gefunden: Europa/Paris, Berlin, MEZ, … sind in pytz komplett verwanzt 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: verzichte komplett auf pytz!

    – herve-guerin

    29. September 2022 um 10:35 Uhr

Benutzeravatar von unutbu
unutbu

Um ein naives datetime zeitzonenbewusst zu machen, verwenden Sie im Allgemeinen die Methode lokalisieren:

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 es nicht wirklich notwendig, sie zu verwenden localize da keine Sommerzeitberechnung zu handhaben ist:

now_aware = unaware.replace(tzinfo=pytz.UTC)

funktioniert. (.replace gibt eine neue Datumszeit zurück; es ändert sich nicht unaware.)

  • Nun, ich fühle mich albern. Replace gibt eine neue datetime zurück. Das steht auch in den Dokumenten und das habe ich komplett übersehen. Danke, genau das habe ich gesucht.

    – Markus Tozzi

    15. August 2011 um 14:24 Uhr

  • “Ersetzen gibt eine neue Datumszeit 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 Uhr

  • 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 mehrdeutig sein kann. tz.localize(..., is_dst=None) behauptet, dass dies nicht der Fall ist.

    – jfs

    8. März 2014 um 21:10 Uhr

  • putz hat einen Fehler, der die Zeitzone von Amsterdam auf + 20 Minuten nach UTC setzt, eine archaische Zeitzone von 1937. Sie hatten einen Job pytz.

    – Boris

    18. Februar 2020 um 16:09 Uhr

kangs Benutzeravatar
kang

Alle diese Beispiele verwenden ein externes Modul, aber Sie können das gleiche Ergebnis nur mit dem datetime-Modul erzielen, 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 Verhinderung der pytz Probleme, ich bin froh, dass ich ein bisschen nach unten gescrollt habe! Wollte nicht mit anpacken pytz auf meinen Remote-Servern in der Tat 🙂

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

  • Das solltest du beachten dt.replace(tzinfo=timezone.utc) gibt eine neue datetime zurück, sie ändert sich nicht dt an Ort und Stelle. (Ich werde bearbeiten, um dies zu zeigen).

    – Blairg23

    28. März 2017 um 0:58 Uhr

  • Wie könnten Sie anstelle von timezone.utc eine andere Zeitzone als Zeichenfolge angeben (z. B. “America/Chicago”)?

    – Tölpel

    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

Benutzeravatar von Sérgio
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, wo localtz war UTC, weil UTC keine DST-Übergänge hat

    – jfs

    15. Mai 2014 um 19:55 Uhr


  • @JF Sebastian , erster Kommentar angewendet

    – Sergio

    15. Mai 2014 um 20:59 Uhr

  • Ich weiß es zu schätzen, dass Sie beide Richtungen der Konvertierung zeigen.

    – Christian Lange

    24. Juni 2016 um 1:08 Uhr

  • @Sérgio und wenn Sie dieses Argument in .replace einfügen und “TypeError: replace() got an unknown keyword argument ‘tzinfo'” erhalten? Was kann man gegen dieses Problem tun?

    – Gustavo Rottgering

    22. Juni 2020 um 19:36 Uhr

Benutzeravatar von Googol
Googol

Ich verwende diese Anweisung in Django, um eine unbewusste Zeit in eine bewusste zu konvertieren:

from django.utils import timezone

dt_aware = timezone.make_aware(dt_unaware, timezone.get_current_timezone())

Benutzeravatar von xjcl
xjkl

Python 3.9 fügt die hinzu zoneinfo Modul also wird jetzt nur noch die Standardbibliothek benötigt!

from zoneinfo import ZoneInfo
from datetime import datetime
unaware = datetime(2020, 10, 31, 12)

Zeitzone anhängen:

>>> 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 korrekt in 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 wird hier ein zusätzliches Paket benötigt:

pip install tzdata  

Es gibt einen Backport, um die Verwendung zu ermöglichen zoneinfo In Python 3.6 bis 3.8:

pip install backports.zoneinfo

Dann:

from backports.zoneinfo import ZoneInfo

  • Unter Windows müssen Sie dies ebenfalls 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. Wissen Sie, wie die Situation auf Macs ist?

    – xjkl

    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. Es wird nicht schaden zu installieren tzdata unbedingt (da die Systemdaten priorisiert werden tzdata), wenn Ihre Anwendung Zeitzonendaten benötigt.

    – Paulus

    21. September 2020 um 19:29 Uhr

  • @xjcl tzdata ist effektiv Teil der Standardbibliothek (wir nennen es ein “Erstanbieterpaket”), Sie müssen es nur per Pip installieren, da es eine viel schnellere Veröffentlichungsfrequenz als CPython hat.

    – Paulus

    27. September 2020 um 0:18 Uhr

  • Ich wollte erwähnen, dass es sehr einfach ist, dies nur für Windows bedingt zur requirements.txt hinzuzufügen: tzdata; sys_platform == "win32" (aus: 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 gängiges Szenario, dass Menschen mit einem tz-bewussten Wert arbeiten, der einen hat datetime, die eine andere lokale Zeitzone als UTC hat.

Wenn Sie nur nach Namen gehen würden, würde man wahrscheinlich folgern, dass replace() anwendbar ist und das richtige datetime-bewusste Objekt erzeugt. Das ist nicht der Fall.

das replace( tzinfo=… ) scheint in seinem Verhalten zufällig zu sein. Es ist daher nutzlos. Verwenden Sie dies nicht!

localize ist die richtige zu verwendende 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 zeitzonenbewussten datetime-Wert 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 ebenfalls 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. Wissen Sie, wie die Situation auf Macs ist?

    – xjkl

    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. Es wird nicht schaden zu installieren tzdata unbedingt (da die Systemdaten priorisiert werden tzdata), wenn Ihre Anwendung Zeitzonendaten benötigt.

    – Paulus

    21. September 2020 um 19:29 Uhr

  • @xjcl tzdata ist effektiv Teil der Standardbibliothek (wir nennen es ein “Erstanbieterpaket”), Sie müssen es nur per Pip installieren, da es eine viel schnellere Veröffentlichungsfrequenz als CPython hat.

    – Paulus

    27. September 2020 um 0:18 Uhr

  • Ich wollte erwähnen, dass es sehr einfach ist, dies nur für Windows bedingt zur requirements.txt hinzuzufügen: tzdata; sys_platform == "win32" (aus: 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 Verwendung von 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 konvertiert zuerst Ihre datetime Objekt auf UTC dann in die Zeitzone, was dasselbe ist wie ein Anruf datetime.replace mit den ursprünglichen Zeitzoneninformationen None.

  • Wenn Sie es UTC machen wollen: .replace(tzinfo=dateutil.tz.UTC)

    – Martin Thoma

    19. August 2019 um 6:08 Uhr

  • Ein Import weniger und nur: .replace(tzinfo=datetime.timezone.utc)

    – Kubanczyk

    9. April 2020 um 15:16 Uhr

1449090cookie-checkWie mache ich ein Datetime-Objekt in Python bewusst (nicht naiv)?

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

Privacy policy