Zeitstempel, Zeitzonen und die Stunde, die zweimal passiert

Was die Unix-Zeit tatsächlich zählt, wie sich Offsets nach ISO 8601 von IANA-Zeitzonen unterscheiden, was die Sommerzeit mit Weckern und Zeiträumen anstellt und wo UTC zu speichern die falsche Antwort ist.

Ein Zeitstempel beantwortet eine von zwei Fragen, und die meisten Datumsfehler entstehen, weil beide vermischt werden. Er benennt entweder einen Zeitpunkt, eine Stelle auf der Zeitachse, über die sich jeder Beobachter einig ist, oder eine Uhrzeitablesung, die nichts bedeutet, solange du nicht weißt, wessen Uhr gemeint ist. Eine Zahlung geschah zu einem Zeitpunkt; ein Zahnarzttermin ist eine Uhrzeitablesung. Speichere das eine als das andere, und es geht später kaputt, um genau eine Stunde, an einem Sonntagmorgen.

Unix-Zeit zählt, sie sagt nicht die Uhrzeit

Die Unix-Zeit ist die Anzahl der Sekunden, die seit 1970-01-01 00:00:00 UTC vergangen sind. Dieses UTC sagt, wo die Zählung begann; der Wert selbst trägt keine Zeitzone. Derselbe Zeitpunkt ist in Amsterdam, Auckland und Lima dieselbe Zahl.

Das Problem ist die Einheit, also zähle die Stellen.

StellenBeispielEinheit
101756080000Sekunden
131756080000000Millisekunden

Jedes heutige Datum hat 10 Stellen in Sekunden und 13 in Millisekunden, und das bleibt bis 2286 so. Beide Fehllesarten sind laut: Millisekunden als Sekunden gelesen landen im Jahr 57626, Sekunden als Millisekunden gelesen im Januar 1970. Ein Zeitstempelkonverter, der das interpretierte Datum anzeigt, macht das offensichtlich.

Die andere Grenze ist der Behälter. Eine Sekundenzählung mit Vorzeichen und 32 Bit endet bei 2147483647, das ist der 19. Januar 2038 um 03:14:07 UTC, danach schlägt sie ins Negative um und liest sich als Dezember 1901. Alles, was eine Gültigkeit von 30 Jahren speichert, rechnet heute schon darüber hinaus. 32 Bit ohne Vorzeichen reichen bis 2106, verlieren aber jedes Datum vor 1970; 64 Bit haben keine praktische Obergrenze.

ISO 8601 und RFC 3339

Die Form, die du dir merken solltest, ist 2026-08-25T14:30:00Z: Datum, ein T, Uhrzeit, dann eine Zonenkennung. RFC 3339 ist ein strengeres Profil von ISO 8601 für Internetprotokolle, und es verlangt diese Kennung. ISO 8601 selbst erlaubt außerdem Wochendaten, eine Form ohne Trennzeichen wie 20260825T143000Z und Zeitstempel ohne Offset; eine API, die „ISO 8601“ verspricht, meint also meist die Teilmenge nach RFC 3339.

Z bedeutet einen Offset von null. +02:00 bedeutet, dass die lokale Uhr UTC um zwei Stunden voraus ist, UTC findest du also durch Abziehen; das umzudrehen ist ein verbreiteter Fehler um zwei Stunden.

ZeichenketteWas sie benennt
2026-08-25T14:30:00ZEinen Zeitpunkt, eindeutig
2026-08-25T16:30:00+02:00Denselben Zeitpunkt, auf einer Uhr, die zwei Stunden voraus ist
2026-08-25T14:30:00Eine Uhrzeitablesung, keinen Zeitpunkt
2026-08-25Ein Datum, 23, 24 oder 25 Stunden lang

Eine Zeichenkette ohne Z und ohne Offset ist mehrdeutig, und die Parser sind sich uneins: Manche nehmen UTC an, andere die Systemzone. Deshalb wird ein reiner Datumswert wie ein Geburtstag westlich von Greenwich oft einen Tag zu früh dargestellt.

Ein Offset ist keine Zeitzone

+02:00 ist eine Tatsache über einen Zeitpunkt an einem Ort. Europe/Amsterdam ist ein Regelwerk: +01:00 im Winter, +02:00 im Sommer und eine andere Gestalt in früheren Jahrzehnten. Den Offset zu speichern bewahrt die Antwort für den Zeitpunkt, an dem er gemessen wurde, und wirft die Fähigkeit weg, irgendeine andere zu berechnen.

Offsets sind außerdem nicht eindeutig. An einem Sommernachmittag deckt +02:00 Amsterdam, Lagos und Johannesburg ab, aus dem Offset allein führt also kein Weg zurück zu den Regeln, und es lässt sich nicht sagen, was diese Uhr im nächsten Dezember anzeigt. Abkürzungen sind noch schlimmer, denn CST benennt drei verschiedene Zonen. Speichere die IANA-Kennung in der Form Area/Location.

Die Stunde, die es nicht gibt, und die Stunde, die zweimal passiert

In der gesamten EU werden die Uhren am letzten Sonntag im März vorgestellt und am letzten Sonntag im Oktober zurückgestellt. In Amsterdam wird im Frühjahr aus 02:00 die Zeit 03:00, jede lokale Zeit von 02:00:00 bis 02:59:59 kommt an diesem Tag also nicht vor. Im Herbst wird aus 03:00 die Zeit 02:00, 02:30 passiert also zweimal, einmal bei +02:00 und eine Stunde später erneut bei +01:00.

  • Wiederkehrende Wecker. Ein Job, der auf 02:30 Ortszeit gesetzt ist, hat am Frühjahrssonntag keine gültige Zeit. Scheduler verhalten sich verschieden: überspringen, auf 03:00 verschieben oder auf 01:30 verschieben. Am Herbstsonntag löst derselbe Job womöglich einmal oder zweimal aus; prüfe die nächsten paar Auslösezeitpunkte mit einem Cron-Parser.
  • Logdateien. Eine Stunde lokaler Zeitstempel ohne Offset lässt sich nicht sortieren, und zwei Ereignisse im Abstand einer Stunde tragen identischen Text. Logge in UTC, oder schreibe den Offset in jede Zeile.
  • Zeiträume. Von 20:00 Uhr am 24. Oktober 2026 bis 20:00 Uhr am Folgetag sind es in Amsterdamer Ortszeit 25 Stunden. „Einen Tag addieren“ und „24 Stunden addieren“ sind verschiedene Operationen, und eine Datumsdifferenz in Kalendertagen beantwortet eine andere Frage als ein Zeitraumkonverter, der in Stunden rechnet.

Die Regeln ändern sich, die Speicherung muss das überstehen

Die IANA-Zeitzonendatenbank, tzdata, enthält diese Regeln und wird mehrmals im Jahr veröffentlicht, denn die Regeln sind Gesetzgebung: Regierungen schaffen die Sommerzeit ab, führen sie ein oder verschieben die Umstellungstermine, manchmal mit wenigen Wochen Vorlauf. Ein festgepinntes Container-Image, ein Browser und eine Sprachlaufzeit tragen jeweils ihre eigene Kopie und können sich auf einer Maschine widersprechen. Eine künftige Ortszeit in einen Zeitpunkt umzurechnen ist eine Vorhersage, die erst feststeht, wenn er eintritt.

Art des WertsSpeichernGrund
Etwas, das geschehen istZeitpunkt in UTCDie Vergangenheit lässt sich nicht neu beschließen
Künftiger Termin nach der UhrLokales Datum mit Uhrzeit plus IANA-ZonenkennungGemeint ist die Uhrzeitablesung
Künftiger Zeitpunkt, über alle Zonen hinweg festUTCGemeint ist der Zeitpunkt
Datum ohne UhrzeitReines Datum, keine Zone, keine MitternachtEine Zone anzuhängen lässt es driften

Deshalb ist „speichere einfach UTC“ für ein Meeting im nächsten Jahr falsch. Rechne heute 09:00 Uhr am 30. März 2027 in Europe/Amsterdam nach UTC um, und die Antwort ist 07:00Z. Wenn sich der Umstellungstermin bis dahin verschiebt, findet das Meeting weiterhin um 09:00 Ortszeit statt, aber 07:00Z stellt sich nun als 08:00 dar: Der gespeicherte Wert hat eine Vorhersage eingefroren und die Absicht weggeworfen. Behalte Ortszeit plus Zone als maßgebliche Quelle; jede UTC-Spalte daneben ist ein Cache.

Datumsangaben für Menschen anzeigen

03/04 lässt sich aus der Zeichenkette nicht auflösen. Für den größten Teil der Welt ist es der 3. April und in den Vereinigten Staaten der 4. März; 03/04/05 fügt eine dritte Unbekannte hinzu. Schreibe den Monat für die Ausgabe an Menschen als Wort (4 Apr 2026) und nutze ISO für den Austausch zwischen Maschinen. Ein Discord-Zeitstempel-Tag umgeht das im Chat, indem er in Zone und Locale desjenigen dargestellt wird, der ihn liest: Speichere einen eindeutigen Wert und formatiere am Rand.

Wochennummern haben ihre eigene Regel. ISO-Wochen beginnen am Montag, und Woche 1 ist die Woche, die den ersten Donnerstag im Januar enthält, gleichbedeutend mit der Woche, die den 4. Januar enthält. Der 1. Januar kann also in Woche 52 oder 53 des vorherigen ISO-Jahres fallen und der 29. Dezember in Woche 1 des nächsten. Eine verbreitete nordamerikanische Konvention nummeriert stattdessen ab der am Sonntag beginnenden Woche, die den 1. Januar enthält, dasselbe Datum bekommt also zwei Nummern, und es lohnt sich, die ISO-Woche eines Wochentagsrechners gegen eine Tabellenkalkulation zu prüfen.

Zeiträume, Monate und Schaltjahre

„Einen Monat später“ ist keine feste Anzahl von Tagen; es sind 28, 29, 30 oder 31. Einen Monat zum 31. Januar zu addieren hat keine richtige Antwort, und Bibliotheken kappen in der Regel auf das Ende des Februars, was die Operation nicht umkehrbar macht: einen Monat addieren, ihn wieder abziehen, und aus dem 31. Januar ist der 28. Januar geworden. Das Runden ist dieselbe Falle im Kleinen: Eine Uhrzeit auf die nächste Viertelstunde abzuschneiden und auf sie zu runden unterscheiden sich um bis zu ein volles Intervall.

Die Schaltjahrregel vollständig: Ein Jahr ist ein Schaltjahr, wenn es durch 4 teilbar ist, außer dass durch 100 teilbare Jahre es nicht sind, es sei denn, sie sind auch durch 400 teilbar. 1900 war also keines, 2000 war eines, und 2100 wird keines sein, was ein durchschnittliches Jahr von 365,2425 Tagen ergibt. Code, der nur die Teilbarkeit durch 4 prüft, ist für jedes Jahr in lebender Erinnerung richtig, und deshalb überlebt er bis 2100. Ein Jahrestag am 29. Februar ist das verwandte Ärgernis: Das nächste Jahr bietet den 28. Februar und den 1. März, und keine Regel wählt zwischen ihnen, also entscheide dich bewusst.