7 Unix-Timestamp-Bugs, die jeder Entwickler schon ausgeliefert hat
Das sind die Timestamp-Fehler, die Produktionsvorfälle verursachen: stille Einheiten-Diskrepanzen, Zeitzonen-Annahmen, das vergessene ×1000, String-Speicherung, mehrdeutiges Parsen und Sommerzeit-Grenzfehler. Jeder Bug enthält das Symptom, die Ursache und einen praktischen Fix.
Bug 1: Fehlendes × 1000 in JavaScript
new Date(1700000000) erzeugt ein Datum nahe Januar 1970, nicht November 2023. Der Date-Konstruktor von JavaScript erwartet Millisekunden, aber die meisten Server-APIs und Datenbanken liefern Unix-Sekunden. Dieser Bug zeigt sich oft zuerst in Admin-Dashboards, weil das Backend-Payload gültiges JSON ist und die UI einfach das falsche Jahr rendert.
- Wrong: new Date(response.created_at) — if created_at is Unix seconds
- Right: new Date(response.created_at * 1000)
- Detection: if your date shows a year near 1970, the timestamp is in seconds not milliseconds
- Safe wrapper: const toDate = ts => new Date(ts < 1e11 ? ts * 1000 : ts)
Bug 2: Gemischte Einheiten an einer Systemgrenze
JavaScript liefert Millisekunden. Python, Go und die meisten serverseitigen APIs liefern Sekunden. Wenn JavaScript einen Timestamp ohne Umwandlung an ein Python-Backend sendet, interpretiert das Backend einen Millisekundenwert als Sekunden und platziert das Ereignis im Jahr 55792. Die umgekehrte Richtung ist genauso schlimm: Die UI erhält Sekunden, gibt sie direkt an new Date() und zeigt Januar 1970.
- JavaScript sends: Date.now() → 1700000000000 (ms)
- Python receives: datetime.fromtimestamp(1700000000000) → year 55792
- Fix: divide by 1000 before sending to a non-JavaScript system
- Better fix: use ISO 8601 strings at API boundaries — they are unambiguous
Bug 3: Sich auf die lokale Zeitzone des Servers verlassen
Code, der Pythons datetime.fromtimestamp() ohne UTC-Angabe nutzt, liefert je nach konfigurierter Server-Zeitzone unterschiedliche Ergebnisse. Es funktioniert in der Entwicklung und bricht in Produktion, wenn in einer anderen Region deployt wird. Dieselbe Bug-Kategorie tritt in Node auf, wenn Code Daten ohne explizite timeZone-Option formatiert und das Ergebnis dann in Tests einfriert.
- Wrong: datetime.fromtimestamp(ts) — uses server's local timezone
- Right: datetime.fromtimestamp(ts, tz=datetime.timezone.utc)
- Wrong in JS: date.toLocaleDateString() — uses the Node process timezone
- Right in JS: date.toISOString() or Intl.DateTimeFormat with an explicit timeZone option
Bug 4: Timestamps als Strings speichern
Einen Timestamp als formatierten String in einer VARCHAR-Spalte zu speichern wirkt harmlos, bis man nach Zeit sortieren, einen Bereich abfragen oder rechnen muss. String-Vergleich von Daten funktioniert nur mit strikten, nullgefüllten ISO-8601-Feldern. Lesbare Strings wie Nov 15, 2023 oder 1/2/24 sind Anzeigeformate, keine Speicherformate.
- Wrong: INSERT INTO events (created) VALUES ('Nov 15, 2023') — not sortable
- Wrong: INSERT INTO events (created) VALUES ('2023-11-15') — loses time component
- Right: use a native DATETIME/TIMESTAMPTZ column or BIGINT for Unix milliseconds
- If a string is unavoidable: use ISO 8601 with full precision: '2023-11-15T06:13:20Z'
Bug 5: Mehrdeutige Datums-Strings parsen
new Date('2024-01-01') und new Date('2024/01/01') sehen gleich aus, verhalten sich aber unterschiedlich. Das ISO-8601-Bindestrichformat wird als UTC-Mitternacht geparst; das Schrägstrichformat ist implementierungsabhängig und die meisten Browser parsen es als lokale Mitternacht. Ein Datumswähler, ein API-Payload und eine Datenbankzeile können dasselbe Kalenderdatum zeigen und doch verschiedene Momente darstellen.
- new Date('2024-01-01') → January 1, 2024 00:00:00 UTC (correct, explicit)
- new Date('2024/01/01') → January 1, 2024 00:00:00 local time (varies by runtime)
- new Date('January 1, 2024') → local time, may be rejected by strict parsers
- Safe rule: always use ISO 8601 with explicit timezone: new Date('2024-01-01T00:00:00Z')
Bug 6: 24 Stunden addieren für morgen
86.400.000 Millisekunden zu addieren wirkt wie ein sauberer Weg zu morgen, aber lokale Kalendertage sind nicht immer 24 Stunden lang. Bei Sommerzeit-Umstellungen kann ein lokaler Tag 23 oder 25 Stunden haben. Wenn die Produktlogik den nächsten lokalen Kalendertag meint, nutze Kalenderarithmetik in der Zielzeitzone, nicht Dauerarithmetik in UTC-Millisekunden.
- Falsch für Kalendertage: next = new Date(date.getTime() + 86400000)
- Besser in lokalem Date-Code: kopiere das Date, dann rufe setDate(d.getDate() + 1) auf
- Für Server-Code: nutze eine zeitzonenbewusste Bibliothek oder Datenbank-Datumsarithmetik
- Für wiederkehrende Pläne: speichere local_date, local_time und timezone_id und berechne jedes Vorkommen
- Füge Tests für die Start- und Enddaten der Sommerzeit in der Zielzeitzone hinzu
Bug 7: Inklusive Tagesende-Filter
Eine häufige Analytics-Abfrage nutzt created_at >= start und created_at <= endOfDay. Das wirkt vernünftig, erzeugt aber Präzisions-Bugs, wenn die Datenbank Millisekunden, Mikrosekunden oder Nanosekunden speichert. Das sicherere Muster ist ein halboffener Bereich: größer oder gleich dem Start und strikt kleiner als der Start der nächsten Periode.
- Risky: WHERE created_at <= '2026-05-19 23:59:59'
- Safer: WHERE created_at >= '2026-05-19' AND created_at < '2026-05-20'
- Half-open ranges work for seconds, milliseconds, microseconds, and nanoseconds
- The same pattern works for months: created_at >= monthStart AND created_at < nextMonthStart
Checkliste zur Vermeidung in Produktion
Die meisten Timestamp-Vorfälle sind durch Benennung, Tests und explizite Grenzen vermeidbar. Das Ziel ist nicht, jede Zeile Datums-Code clever zu machen; es ist, Einheit, Zeitzone und Bereichssemantik langweilig offensichtlich zu machen.
- Benenne numerische Felder mit ihrer Einheit: createdAtMs, expiresAtSeconds, imported_at_ms
- Nutze UTC zum Speichern und explizite IANA-Zeitzonen zur Anzeige
- Bevorzuge ISO-8601-Strings mit Z oder Offsets an von Menschen geprüften API-Grenzen
- Nutze halboffene Bereiche für Datumsfilter
- Teste rund um den 1. Januar, Schalttage, Sommerzeit-Umstellungen und die Jahr-2038-Grenze
FAQ zu Unix-Timestamp-Bugs
- Was ist der häufigste Unix-Timestamp-Bug?
- Sekunden zu übergeben, wo Millisekunden erwartet werden, oder umgekehrt. In JavaScript landet new Date(unixSeconds) in 1970, weil der Konstruktor Millisekunden erwartet; multipliziere den 10-stelligen Wert zuerst mit 1000.
- Wie verhindere ich Sekunden-vs-Millisekunden-Bugs?
- Benenne Felder mit ihrer Einheit (createdAtMs, expiresAtSeconds), wandle an Systemgrenzen um und bevorzuge ISO-8601-Strings in API-Verträgen, damit der Wert selbsterklärend ist.
- Warum bricht das Addieren von 86.400.000 ms „morgen“?
- Lokale Kalendertage dauern bei Sommerzeit-Umstellungen 23 oder 25 Stunden, also kann das Addieren fester 24 Stunden auf das falsche Datum fallen. Nutze Kalenderarithmetik in der Zielzeitzone.