Unix-Zeitstempel verstehen: Ein Leitfaden für Entwickler
Was Unix-Zeitstempel sind, wie sie funktionieren, Schaltsekunden, das 2038-Problem und praktische Umrechnung.
Warum Entwickler Unix-Zeitstempel verstehen sollten
Ein Unix-Zeitstempel ist eine Ganzzahl, die die Anzahl der Sekunden seit 00:00:00 UTC am 1. Januar 1970 (der Unix-Epoche) darstellt. Sie sind die lingua franca der Zeit in der Informatik: Datenbanken indizieren sie, APIs geben sie zurück, Dateisysteme speichern sie, und Protokolle wie NTP und HTTP verwenden sie. Jeder Entwickler muss irgendwann zwischen Unix-Zeitstempeln und für Menschen lesbaren Daten umrechnen — und die Probleme debuggen, die entstehen, wenn verschiedene Systeme die Zeit unterschiedlich darstellen.
Dieser Leitfaden behandelt, was Unix-Zeitstempel sind, wie sie in verschiedenen Sprachen funktionieren, die Komplikationen mit Zeitzonen und Schaltsekunden, und das kommende 2038-Problem.
Wie Unix-Zeitstempel funktionieren
Ein Unix-Zeitstempel ist nur ein ganzzahliger Sekundenzähler. Es gibt keine Zeitzone, keine Sommerzeit, keine Kalenderarithmetik. Das Addieren von 86.400 zu einem Zeitstempel rückt das Datum in UTC immer genau einen Tag vor.
``` 0 = 1970-01-01 00:00:00 UTC (die Epoche) 1,000 = 1970-01-01 00:16:40 UTC 1,000,000 = 1970-01-12 13:46:40 UTC 1,000,000,000 = 2001-09-09 01:46:40 UTC (das „Milliardenste") 1,500,000,000 = 2017-07-14 02:40:00 UTC 2,000,000,000 = 2033-05-18 03:33:20 UTC 2,147,483,647 = 2038-01-19 03:14:07 UTC (32-Bit signed max) ```
Der größte darstellbare Wert hängt vom Ganzzahltyp ab:
- 32-Bit signed: 2.147.483.647 (2^31 − 1) → 2038-01-19 03:14:07 UTC
- 32-Bit unsigned: 4.294.967.295 (2^32 − 1) → 2106-02-07 06:28:15 UTC
- 64-Bit signed: 9.223.372.036.854.775.807 → Jahr 292.277.026.596
Deshalb gibt es das 2038-Problem: Jeder 32-Bit-signed-Zeitstempel läuft am 2038-01-19 über. 64-Bit-Zeitstempel vermeiden das Problem für Hunderte von Milliarden Jahren.
Unix-Zeit vs. UTC: das Schaltsekunden-Problem
Streng genommen ignoriert die Unix-Zeit Schaltsekunden. Sie zählt jede Sekunde, als gäbe es genau 86.400 pro Tag, selbst wenn die internationalen Zeitbehörden eine Schaltsekunde zur UTC hinzufügen. Nachdem 27 Schaltsekunden eingefügt wurden (Stand 2024), ist die Unix-Zeit jetzt etwa 27 Sekunden vor der „echten" UTC.
In der Praxis spielt dies selten eine Rolle. TAI (Internationale Atomzeit) hält perfekte Sekunden, und UTC bleibt durch das Hinzufügen von Schaltsekunden innerhalb von 0,9 Sekunden von UT1 (Erdrotationszeit). Die Unix-Zeit verwendet einfach den Sekundenzähler von TAI minus einen festen Versatz.
Wenn Sie strikte Atomzeit benötigen, verwenden Sie TAI direkt. Für alles andere ist die kleine Drift der Unix-Zeit irrelevant.
Zeitzonen und Versätze
Ein Unix-Zeitstempel bezieht sich auf einen einzigen Zeitpunkt. Um ihn in einer für Menschen lesbaren Form anzuzeigen, wenden Sie einen Zeitzonen-Versatz an. Häufige Versätze:
| Zone | Abkürzung | Versatz von UTC | |------|------------|-----------------| | Koordinierte Weltzeit | UTC | 0 | | US-Osten (Winter) | EST | −5 | | US-Osten (Sommer) | EDT | −4 | | US-Pazifik (Winter) | PST | −8 | | US-Pazifik (Sommer) | PDT | −7 | | Vereinigtes Königreich (Winter) | GMT | 0 | | Vereinigtes Königreich (Sommer) | BST | +1 | | Mitteleuropa (Winter) | CET | +1 | | Mitteleuropa (Sommer) | CEST | +2 | | Japan | JST | +9 | | China | CST | +8 | | Indien | IST | +5:30 | | Australien (Sydney, Winter) | AEST | +10 | | Australien (Sydney, Sommer) | AEDT | +11 |
Beachten Sie, dass Indien und China einen ganzjährigen einzelnen Versatz verwenden (keine Sommerzeit). Die USA, das Vereinigte Königreich und der größte Teil Europas beobachten die Sommerzeit, was alles kompliziert.
Ein häufiger Fehler: Ein Server speichert einen Unix-Zeitstempel (immer UTC), zeigt ihn dann ohne Anwendung des lokalen Versatzes des Benutzers an, und der Benutzer sieht eine um mehrere Stunden verschobene Zeit. Speichern Sie immer in UTC, konvertieren Sie immer zur Anzeigezeit in lokal.
Das 2038-Problem im Detail
Das 2038-Problem (auch Y2K38 genannt) ist der 32-Bit-signed-Ganzzahlüberlauf, der am 2038-01-19 03:14:07 UTC auftreten wird. Die nächste Sekunde läuft zu −2.147.483.648 über, was auf den meisten Systemen als 1901-12-13 20:45:52 UTC interpretiert wird. Alles, was von Unix-Zeitstempeln als 32-Bit-signed-Ganzzahlen abhängt, wird kaputtgehen:
- Dateisysteme, die mtime in 32-Bit-time_t speichern
- Ältere Datenbanken (altes MySQL, einige SQLite-Builds)
- Eingebettete Systeme (Router, IoT-Geräte, Autos)
- Netzwerkprotokolle, die 32-Bit-Zeitfelder verwenden (NTP, DNS, Kerberos, einige TLS-Handshakes)
Moderne Systeme (64-Bit-Linux, 64-Bit-macOS, modernes Windows, moderne Datenbanken) sind bereits auf 64-Bit migriert. Das Risiko liegt in Legacy- und eingebettetem Code, der nicht aktualisiert wurde.
Die Lösung: Ändern Sie den Datentyp von `time_t` (32-Bit) zu `int64_t` (oder gleichwertig). Eine Deklarationsänderung, plus eine Neukompilierung. Die Herausforderung besteht darin, jede Stelle zu finden, an der die 32-Bit-Annahme lebt — Dateiformate, Netzwerkprotokolle, persistierte Daten, Drittanbieter-Bibliotheken.
Arbeiten mit Unix-Zeitstempeln im Code
Python
Pythons `datetime`-Modul ist das kanonische Werkzeug.
```python import datetime from zoneinfo import ZoneInfo
# Aktueller Unix-Zeitstempel (Sekunden, float) import time now = time.time() # 1700000000.123
# Unix-Zeitstempel in datetime umrechnen (UTC) ts = 1700000000 utc = datetime.datetime.fromtimestamp(ts, tz=datetime.timezone.utc) # 2023-11-14 22:13:20+00:00
# Unix-Zeitstempel in datetime umrechnen (bestimmte Zone) ny = datetime.datetime.fromtimestamp(ts, tz=ZoneInfo("America/New_York")) # 2023-11-14 17:13:20-05:00
# datetime in Unix-Zeitstempel umrechnen d = datetime.datetime(2023, 11, 14, 22, 13, 20, tzinfo=datetime.timezone.utc) ts = int(d.timestamp()) # 1700000000
# Als ISO 8601 formatieren print(utc.isoformat()) # 2023-11-14T22:13:20+00:00 ```
Vorsicht: `datetime.fromtimestamp(ts)` (ohne `tz`) verwendet die lokale Zeitzone, was in einem Server-Kontext selten das ist, was Sie wollen. Übergeben Sie immer eine explizite Zeitzone.
JavaScript
JavaScript verwendet Millisekunden, nicht Sekunden. `Date.now()` gibt Millisekunden seit der Epoche zurück.
```js // Aktueller Unix-Zeitstempel in Millisekunden const now = Date.now(); // z. B. 1700000000000
// Aktueller Unix-Zeitstempel in Sekunden const nowSec = Math.floor(Date.now() / 1000);
// Sekunden in Millisekunden umrechnen und Date erstellen const date = new Date(1700000000 * 1000); // 2023-11-14T22:13:20.000Z
// Millisekunden in Sekunden umrechnen const ts = Math.floor(date.getTime() / 1000);
// In einer bestimmten Zeitzone mit Intl anzeigen console.log(date.toLocaleString("en-US", { timeZone: "Asia/Tokyo" })); // 11/15/2023, 7:13:20 AM
// ISO 8601 console.log(date.toISOString()); // 2023-11-14T22:13:20.000Z ```
Die größte JavaScript-Falle: Alle anderen Sprachen verwenden Sekunden; JavaScript verwendet Millisekunden. Das Vergessen der Multiplikation oder Division mit 1000 ist die Quelle unzähliger „um den Faktor 1000 daneben"-Bugs.
SQL
Die meisten Datenbanken unterstützen Unix-Zeitstempel nativ oder über Funktionen.
```sql -- MySQL SELECT UNIX_TIMESTAMP(NOW()); -- aktueller Zeitstempel (Sekunden) SELECT FROM_UNIXTIME(1700000000); -- 2023-11-14 22:13:20 SELECT FROM_UNIXTIME(1700000000, '%Y-%m-%d %H:%i:%s');
-- PostgreSQL SELECT EXTRACT(EPOCH FROM NOW()); -- aktueller Zeitstempel (Sekunden, float) SELECT TO_TIMESTAMP(1700000000); -- 2023-11-14 22:13:20+00
-- SQLite SELECT strftime('%Y-%m-%dT%H:%M:%fZ', 'unixepoch', 1700000000); -- 2023-11-14T22:13:20.000Z ```
MySQLs `UNIX_TIMESTAMP()` gibt Sekunden zurück; PostgreSQLs `EXTRACT(EPOCH FROM ...)` gibt Sekunden mit Bruchteilgenauigkeit zurück. SQLite erfordert das manuelle `strftime`-Rezept.
Bash
Der Befehl `date` verarbeitet Unix-Zeitstempel auf jedem Unix-System.
```bash # Aktueller Unix-Zeitstempel date +%s # 1700000000
# Unix-Zeitstempel in eine Datumszeichenfolge umrechnen # Linux (GNU date) date -d @1700000000 # Tue Nov 14 22:13:20 PM UTC 2023
# macOS (BSD date) date -r 1700000000 # Tue Nov 14 22:13:20 UTC 2023
# Datumszeichenfolge in Unix-Zeitstempel umrechnen # Linux date -d "2023-11-14 22:13:20 UTC" +%s # macOS date -j -f "%Y-%m-%d %H:%M:%S" "2023-11-14 22:13:20" +%s
# In einer bestimmten Zeitzone anzeigen TZ="Asia/Tokyo" date -d @1700000000 # Wed Nov 15 07:13:20 JST 2023 ```
Als Kurzreferenz: `date +%s` für „jetzt als Unix-Zeit" ist ein Einzeiler, den Sie wöchentlich verwenden werden.
Zeitstempel in Datenbanken speichern
Best Practices für die Datenbankspeicherung:
- Als Ganzzahlen (BIGINT) speichern: Native Unix-Zeitstempel sind günstig zu speichern, leicht zu indizieren, einfach zu vergleichen.
- Immer UTC verwenden: Erst zur Anzeigezeit in die lokale Zone umrechnen.
- BIGINT verwenden, nicht INT: Eine 32-Bit-signed-Ganzzahl läuft 2038 über. Verwenden Sie immer 64-Bit.
- Millisekunden-Genauigkeit erwägen: Für Hochfrequenz-Ereignisprotokollierung (Trading, Gaming, Telemetrie) ist 1-Sekunden-Auflösung zu grob. Als BIGINT-Millisekunden speichern.
- TIMESTAMP WITH TIME ZONE verwenden: PostgreSQLs `timestamptz` ist am robustesten. MySQLs `TIMESTAMP` hat das 2038-Problem; verwenden Sie stattdessen `DATETIME`.
- Zeitstempel-Spalten indizieren: Abfragen wie `WHERE created_at > ?` sind extrem häufig. Ein BTREE-Index auf einer Zeitstempel-Spalte macht sie schnell.
Ein gängiges Schema:
```sql CREATE TABLE events ( id BIGSERIAL PRIMARY KEY, event_type TEXT NOT NULL, payload JSONB NOT NULL, created_at BIGINT NOT NULL, -- Unix ms INDEX idx_events_created (created_at) ); ```
Häufige Fallstricke
Der JavaScript-Millisekunden-Bug
JavaScript-Zeitstempel sind in Millisekunden; alle anderen wichtigen Sprachen verwenden Sekunden. Das Mischen erzeugt Zeitstempel, die um den Faktor 1000 daneben liegen.
```js // Von einem Python-Backend const python_ts = 1700000000; // Sekunden const js_date = new Date(python_ts); // 1970-01-20T08:13:20.000Z ← FALSCH!
// Korrekt: mit 1000 multiplizieren const js_date = new Date(python_ts * 1000); ```
Zeitzonen-Anzeigefehler
Ein Zeitstempel bezieht sich auf einen Zeitpunkt, nicht auf ein Datum. Wenn Ihr Server „2023-11-14 22:13:20" protokolliert, ohne UTC oder lokal anzugeben, hat der Benutzer keine Möglichkeit zu wissen, welches es ist. Immer:
- Als Unix-Zeitstempel speichern (inhärent UTC)
- An Clients mit expliziter Zeitzone senden
- Zur Anzeigezeit mit der tatsächlichen Zeitzone des Benutzers in lokal umrechnen
```js // Server sendet "2023-11-14T22:13:20Z" (Z = UTC) // Client rendert in der lokalen Zeitzone des Benutzers const d = new Date("2023-11-14T22:13:20Z"); console.log(d.toLocaleString()); // "11/14/2023, 5:13:20 PM" (in EST) ```
Sommerzeit-Randfälle
Ein 24-Stunden-Tag im Frühling ist tatsächlich 23 Stunden (Sommerzeit überspringt eine Stunde); ein 24-Stunden-Tag im Herbst ist 25 Stunden. Code, der „86.400 addieren, um morgen zu erhalten" annimmt, funktioniert gut; Code, der „24 × 60 × 60 addieren" oder „morgen um 2:30 Uhr" macht, kann bei Sommerzeit-Übergängen fehlschlagen.
```python # Dies ist ein 23- oder 25-Stunden-Tag in Sommerzeit-Zonen d = datetime.datetime(2024, 3, 10, 1, 0, 0, tzinfo=ZoneInfo("America/New_York")) # Sommerzeit beginnt print((d + datetime.timedelta(days=1)).isoformat()) # 2024-03-11T01:00:00-05:00 ← korrekt print((d + datetime.timedelta(hours=24)).isoformat()) # 2024-03-11T02:00:00-04:00 ← Sommerzeit ist gesprungen, dies ist 25 Stunden später ```
Für Planung und Tageszeit-Mathematik bevorzugen Sie Bibliotheken (Luxon, Arrow, date-fns), die diese Randfälle korrekt behandeln.
Methode 1: Den Zeitstempel-Umrechner von UtilBoxx verwenden (Empfohlen)
Für schnelle Umrechnungen während der Entwicklung ist der Zeitstempel-Umrechner von UtilBoxx ein privates, browserbasiertes Werkzeug, das zwischen Unix-Zeitstempeln und Daten in jeder Zeitzone umrechnet, sowohl Sekunden als auch Millisekunden unterstützt und vollständig clientseitig läuft. Kein Upload, keine Registrierung, keine Logs. Setzen Sie ein Lesezeichen für das nächste Mal, wenn Sie einen unbekannten Zeitstempel aus einer API-Antwort dekodieren müssen.
Fazit
Unix-Zeitstempel sind einfach, schnell und universell — aber das umgebende Ökosystem (Zeitzonen, Schaltsekunden, das 2038-Problem, Millisekunden-vs-Sekunden-Verwirrung) ist voller Fallstricke. Jeder Entwickler stößt irgendwann auf jeden davon; die einzige Verteidigung ist zu wissen, dass sie existieren.
Best Practices:
- Als 64-Bit-Ganzzahlen in UTC speichern
- Zur Anzeigezeit in lokal umrechnen mit der tatsächlichen Zeitzone des Benutzers
- Eine echte Zeitzonen-Bibliothek verwenden (Luxon, date-fns-tz, zoneinfo) für nicht-triviale Zeitmathematik
- 32-Bit-Zeitstempel jetzt auditieren wenn Sie Legacy-Code warten
Unix-Zeitstempel sind eines der ältesten Datenformate, die noch verwendet werden, und werden uns alle überleben — aber nur, wenn wir sie richtig verwenden.