Datum und Uhrzeit in Python: naive vs. aware und der Zeitzonen-Fallstrick

Warum datetime.now() ohne Zeitzone eine tickende Zeitbombe ist, was „naive" und „aware" Objekte unterscheidet und wie du mit zoneinfo saubere, DST-sichere Zeitangaben schreibst.

Teilen

Kaum ein Thema sorgt in der Praxis für so viele stille Bugs wie der Umgang mit Datum und Uhrzeit. Ein Termin, der plötzlich eine Stunde verschoben ist. Eine Auswertung, die um Mitternacht das falsche Datum zeigt. Ein Zeitstempel, der auf dem Server anders aussieht als auf dem eigenen Rechner. Fast immer steckt derselbe Denkfehler dahinter: Wir behandeln eine Uhrzeit so, als wäre sie überall auf der Welt dieselbe. Ist sie aber nicht.

In diesem Beitrag schauen wir uns die Grundlagen an, mit denen du diese Fehlerklasse in Python zuverlässig vermeidest.

Naive und aware: der wichtigste Unterschied

Python kennt zwei Sorten von datetime-Objekten. Ein naives Objekt weiß nichts über Zeitzonen – es speichert nur „24. Juli, 14:30 Uhr", ohne zu sagen, in welcher Zeitzone das gilt. Ein aware Objekt trägt diese Information mit sich: „14:30 Uhr in Europe/Berlin".

from datetime import datetime

naiv = datetime.now()        # keine Zeitzone – gefährlich
print(naiv.tzinfo)           # None

Der Klassiker ist datetime.now(). Es liefert die lokale Zeit, aber ohne Zeitzoneninfo. Sobald dieser Wert deinen Rechner verlässt – in eine Datenbank, eine API, eine Log-Datei – geht die Bedeutung verloren. Niemand weiß mehr, ob 14:30 nun Berliner Zeit, UTC oder etwas ganz anderes war.

Zeitzonen mit zoneinfo

Seit Python 3.9 gibt es das Modul zoneinfo, das direkt auf die Zeitzonendatenbank des Betriebssystems zugreift. Damit erstellst du aware Objekte ganz ohne Zusatzpakete:

from datetime import datetime
from zoneinfo import ZoneInfo

jetzt = datetime.now(ZoneInfo("Europe/Berlin"))
print(jetzt.isoformat())     # 2026-07-24T14:30:00+02:00

Das +02:00 am Ende ist der entscheidende Unterschied: Jetzt ist eindeutig festgehalten, welche reale Sekunde gemeint ist. Und weil zoneinfo die echten Regeln kennt, berücksichtigt es auch die Sommerzeit automatisch – im Winter stünde dort +01:00.

Der Umrechnungs-Fallstrick

Ein häufiger Fehler ist, Zeiten „von Hand" umzurechnen, indem man einfach Stunden addiert. Das geht schief, sobald eine Zeitumstellung dazwischenliegt. Richtig macht man es so:

berlin = datetime.now(ZoneInfo("Europe/Berlin"))
new_york = berlin.astimezone(ZoneInfo("America/New_York"))

astimezone() rechnet den gleichen Zeitpunkt in eine andere Zeitzone um – die Uhr zeigt eine andere Zahl, aber es ist dieselbe Sekunde. Genau das will man in fast allen Fällen. Für Speicherung und Vergleich gilt außerdem eine bewährte Faustregel: intern immer in UTC arbeiten (datetime.now(ZoneInfo("UTC"))) und erst bei der Anzeige in die lokale Zeitzone umrechnen.

Noch tiefer: die englischsprachige Vertiefung auf pykit.org

Wer sich richtig festbeißen will – inklusive der Tücken beim Parsen von Zeit-Strings, dem Unterschied zwischen naiven und aware Objekten im Detail und DST-sicherer Arithmetik – findet auf unserem englischsprachigen Schwesterblog eine ausgezeichnete, sehr praxisnahe Vertiefung: Dates and Times Done Right: A Practical Deep Dive into datetime and zoneinfo. Der Artikel geht die typischen Fallen durch, die jeden irgendwann erwischen, und zeigt an lauffähigen Beispielen, wie man sie umgeht. Auch wenn du nur wenig Englisch liest, lohnt sich der Blick auf die Code-Beispiele – sie sprechen für sich.

Kurz zusammengefasst

Nutze niemals blind datetime.now() für Werte, die dein Programm verlassen. Erzeuge stattdessen aware Objekte mit zoneinfo, rechne mit astimezone() um statt mit purer Stundenaddition, und speichere intern am besten in UTC. Mit diesen drei Gewohnheiten verschwindet die große Mehrheit aller Datums-Bugs, bevor sie überhaupt entstehen – und dein zukünftiges Ich dankt es dir bei der nächsten Zeitumstellung.