← Alle Beiträge

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

What is the most common Unix timestamp bug?
Passing seconds where milliseconds are expected, or the reverse. In JavaScript, new Date(unixSeconds) lands in 1970 because the constructor expects milliseconds — multiply the 10-digit value by 1000 first.
How do I prevent seconds-vs-milliseconds bugs?
Name fields with their unit (createdAtMs, expiresAtSeconds), convert at system boundaries, and prefer ISO 8601 strings in API contracts so the value is self-describing.
Why does adding 86,400,000 ms break 'tomorrow'?
Local calendar days are 23 or 25 hours long during daylight saving transitions, so adding a fixed 24 hours can land on the wrong date. Use calendar arithmetic in the target timezone instead.