7 Min

JavaScript Zeitformatierung & Konvertierung: Häufige Fallstricke

Lerne, wie man Zeit in JavaScript zuverlässig formatiert und konvertiert: Timestamps, ISO-Strings, Zeitzonen, Sommerzeit, Parsing-Regeln und praxiserprobte Muster.

JavaScript Zeitformatierung & Konvertierung: Häufige Fallstricke

Was bei JavaScript-Zeit häufig schiefgeht

JavaScript-Zeitfehler sehen selten aus wie „die Uhr geht falsch“. Sie treten als verwirrende kleine Verschiebungen auf: ein Datum, das auf deinem Laptop korrekt ist, aber auf dem Rechner eines Kollegen falsch aussieht, eine API-Antwort, die bis zur Darstellung in einer anderen Zeitzone in Ordnung scheint, oder ein Bericht, der um „einen Tag daneben“ liegt, wenn die Jahreszeit gewechselt wird.

Die häufigsten Symptome

Du wirst typischerweise eines (oder mehrere) dieser Probleme bemerken:

  • Eine Stunde Unterschied: besonders rund um die Sommerzeit (DST) oder wenn ein Wert unbeabsichtigt zwischen Ortszeit und UTC umgerechnet wurde.
  • Ein Tag Unterschied: ein datums-only Wert (z. B. „2025-12-23") erscheint je nach Zeitzone als vorheriger/nächster Tag.
  • Falsche Zeitzone: Zeiten sehen korrekt aus, aber der Offset (z. B. +02:00) ist anders als erwartet.
  • Inkonsistente Formatierung: „funktioniert in Chrome“, sieht in Safari anders aus, oder Server und Browser sind sich beim Parsen einer Zeichenkette uneinig.

Warum das passiert: „Zeit" kann verschiedene Dinge bedeuten

Ein großer Pain-Point ist, dass das Wort Zeit verschiedene Konzepte bezeichnen kann:

  • Ein Zeitpunkt (Instant): ein bestimmter Moment weltweit (z. B. 2025-12-23T10:00:00Z). Das ist meist das, was man für Logging, Events und API-Speicherung möchte.
  • Ein Kalenderdatum: ein Tag im Kalender ohne Zeitzone (z. B. ein Geburtstag oder Rechnungsdatum). So behandelt verschiebt es sich leicht über Tagesgrenzen.
  • Wand-Uhr-Zeit (Wall-clock time): „9:00 AM in Berlin“, abhängig von Zeitzonenregeln und Sommerzeitänderungen.

Das eingebaute Date in JavaScript versucht, all das abzudecken, repräsentiert aber primär ein Instant und neigt gleichzeitig zur lokalen Anzeige, wodurch unbeabsichtigte Konversionen leicht passieren.

Worauf dieser Artikel abzielt

Dieser Leitfaden ist bewusst praktisch: wie man vorhersehbare Konversionen zwischen Browsern und Servern erreicht, wie man sicherere Formate wählt (z. B. ISO 8601) und wie man die klassischen Fallen erkennt (Sekunden vs Millisekunden, UTC vs Lokal, und Parsing-Unterschiede). Ziel ist nicht mehr Theorie — sondern weniger „warum hat sich das verschoben?“ Überraschungen.

Zeitdaten-Typen: Timestamp, Date und String

JavaScript-Zeitfehler beginnen oft damit, dass Repräsentationen vermischt werden, die ähnlich aussehen, es aber nicht sind.

Die drei häufigsten Repräsentationen

1) Epoch-Millisekunden (Zahl)

Eine einfache Zahl wie 1735689600000 ist typischerweise „Millisekunden seit 1970-01-01T00:00:00Z“. Sie repräsentiert ein Instant ohne Formatierung oder Zeitzone.

2) Date-Objekt (Wrapper um ein Instant)

Ein Date speichert denselben Typ Instant wie ein Timestamp. Der verwirrende Teil: wenn du ein Date druckst, formatiert JavaScript es mit den lokalen Regeln deiner Umgebung, sofern du nichts anderes verlangst.

3) Formatierter String (für Menschen)

Strings wie "2025-01-01", "01/01/2025 10:00" oder "2025-01-01T00:00:00Z" sind nicht einheitlich. Einige sind eindeutig (ISO 8601 mit Z), andere hängen von der Locale ab, und einige enthalten gar keine Zeitzonenangabe.

„Instant“ vs „Anzeigezeit"

  • Instant: „dieser exakte Moment global“ (am besten als Epoch-Millisekunden oder ISO-String in UTC gespeichert).
  • Anzeigezeit: „was der Benutzer sehen soll“ (hängt von Locale und Zeitzone ab).

Dasselbe Instant kann je nach Zeitzone unterschiedlich angezeigt werden:

const instant = new Date("2025-01-01T00:00:00Z");

instant.toLocaleString("en-US", { timeZone: "UTC" });
// "1/1/2025, 12:00:00 AM"

instant.toLocaleString("en-US", { timeZone: "America/Los_Angeles" });
// "12/31/2024, 4:00:00 PM" (vorheriger Tag)

Wähle eine „Quelle der Wahrheit"

Wähle eine einzige interne Repräsentation (häufig Epoch-Millisekunden oder UTC ISO 8601) und bleibe in deiner App und API konsistent dabei. Konvertiere zu/von Date und formatierten Strings nur an den Grenzen: beim Input-Parsen und bei der Anzeige im UI.

Timestamps: Sekunden vs Millisekunden (leicht zu verwechseln)

Ein „Timestamp" bedeutet normalerweise Epoch-Zeit (auch Unix-Zeit): die Zeit seit 1970-01-01 00:00:00 UTC. Der Haken: verschiedene Systeme zählen in verschiedenen Einheiten.

JavaScript's Date ist die Quelle der meisten Verwirrung, weil es Millisekunden verwendet. Viele APIs, Datenbanken und Logs verwenden Sekunden.

Faustregel

  • Unix-Timestamp (Sekunden): 1704067200
  • JavaScript-Timestamp (Millisekunden): 1704067200000

Gleicher Moment, aber die Millisekunden-Version hat drei zusätzliche Ziffern.

Sichere Konversionen (Sekunden ↔ Millisekunden)

Verwende explizite Multiplikation/Division, damit die Einheit klar ist:

// seconds -> Date
const seconds = 1704067200;
const d1 = new Date(seconds * 1000);

// milliseconds -> Date
const ms = 1704067200000;
const d2 = new Date(ms);

// Date -> seconds
const secondsOut = Math.floor(d2.getTime() / 1000);

// Date -> milliseconds
const msOut = d2.getTime();

Der klassische Bug: Sekunden an Date() übergeben

Das sieht plausibel aus, ist aber falsch, wenn ts in Sekunden ist:

const ts = 1704067200;      // seconds
const d = new Date(ts);     // WRONG: treated as milliseconds

Das Ergebnis wird ein Datum in 1970 sein, weil 1.704.067.200 Millisekunden nur etwa 19 Tage nach der Epoch darstellen.

Schnelle Validierungs- und Debug-Checks

Wenn du dir nicht sicher bist, welche Einheit du hast, füge schnelle Guardrails hinzu:

function asDateFromUnknownEpoch(x) {
  // crude heuristic: seconds are ~1e9-1e10, milliseconds are ~1e12-1e13
  if (x < 1e11) return new Date(x * 1000); // assume seconds
  return new Date(x);                      // assume milliseconds
}

const input = Number(valueFromApi);
console.log({ input, digits: String(Math.trunc(input)).length });
console.log('as ISO:', asDateFromUnknownEpoch(input).toISOString());

Wenn die „digits“-Zahl ~10 ist, sind es wahrscheinlich Sekunden. Wenn sie ~13 ist, sind es Millisekunden. Gib während des Debuggings toISOString() aus: das ist eindeutig und hilft, Einheit-Fehler sofort zu erkennen.

Ortszeit vs UTC: Warum die Ausgabe verschiebt

JavaScript's Date kann verwirrend sein, weil es intern einen einzelnen Instant speichert, aber diesen Instant in unterschiedlichen Zeitzonen darstellen kann.

Intern ist ein Date im Wesentlichen „Millisekunden seit der Unix-Epoche (1970-01-01T00:00:00Z)“. Diese Zahl repräsentiert einen Moment in UTC. Die Verschiebung entsteht, wenn du JavaScript bittest, diesen Moment als Ortszeit (basierend auf Rechner-/Server-Einstellungen) statt als UTC zu formatieren.

Lokale Getter vs UTC-Getter

Viele Date-APIs haben lokale und UTC-Varianten. Sie liefern unterschiedliche Zahlen für dasselbe Instant:

const d = new Date('2025-01-01T00:30:00Z');

d.getHours();      // Stunde in *lokaler* Zeitzone
d.getUTCHours();   // Stunde in UTC

d.toString();      // lokale Zeit-String
d.toISOString();   // UTC (endet immer mit Z)

Wenn dein Rechner in New York (UTC-5) ist, kann diese UTC-Zeit lokal als „19:30“ am vorherigen Tag erscheinen. Auf einem Server mit UTC-Einstellung erscheint sie als „00:30“. Dasselbe Instant, unterschiedliche Anzeige.

Warum Logs „falsch" aussehen

Logs verwenden oft Date#toString() oder interpolieren ein Date implizit — das nutzt die lokale Zeitzone der Umgebung. Das heißt, derselbe Code kann unterschiedliche Zeitstempel auf deinem Laptop, in CI und in Produktion ausgeben.

Praktische Anleitung

Speichere und übertrage Zeit als UTC (z. B. Epoch-Millisekunden oder ISO 8601 mit Z). Konvertiere in die Benutzer-Locale nur bei der Anzeige:

  • Für APIs: bevorzugt toISOString() oder Epoch-Millisekunden
  • Für das UI: im Benutzer-Zeitzonen-Kontext mit Intl.DateTimeFormat formatieren

Wenn du schnell arbeitest (z. B. mit einem vibe-coding Workflow in Koder.ai), hilft es, das früh in deine API-Verträge einzubauen: benenne Felder klar (createdAtMs, createdAtIso) und sorge dafür, dass Server (Go + PostgreSQL) und Client (React) übereinstimmen, was jedes Feld bedeutet.

ISO 8601-Strings: Das sicherste Format für APIs

Wenn du Daten zwischen Browser, Server und Datenbank senden musst, sind ISO 8601-Strings die sicherste Default-Wahl. Sie sind explizit, breit unterstützt und — am wichtigsten — sie tragen Zeitzoneninformationen.

Verwende explizites UTC oder einen expliziten Offset

Zwei gute Austauschformate:

  • UTC-Zeit (empfohlen, wenn die lokale Zone egal ist): 2025-03-04T12:30:00Z
  • Ortszeit mit Offset (empfohlen, wenn die lokale Uhrzeit wichtig ist): 2025-03-04T12:30:00+02:00

Was bedeutet „Z"?

Z steht für Zulu-Zeit, ein anderer Name für UTC. Also ist 2025-03-04T12:30:00Z „12:30 UTC".

Wann sind Offsets wie +02:00 wichtig?

Offsets sind entscheidend, wenn ein Ereignis an einen lokalen Kontext gebunden ist (Termine, Buchungen, Ladenöffnungszeiten). 2025-03-04T12:30:00+02:00 beschreibt einen Moment, der zwei Stunden vor UTC liegt — und ist nicht dasselbe Instant wie 2025-03-04T12:30:00Z.

Vermeide mehrdeutige Datumsstrings

Strings wie 03/04/2025 sind eine Falle: ist das der 3. März oder der 4. März? Verschiedene Benutzer und Umgebungen interpretieren das unterschiedlich. Bevorzuge 2025-03-04 (ISO-Datum) oder ein vollständiges ISO-Datetime.

Sicheres Round-trip (String → Date → String)

const iso = "2025-03-04T12:30:00Z";
const d = new Date(iso);
const back = d.toISOString();

console.log(iso);  // 2025-03-04T12:30:00Z
console.log(back); // 2025-03-04T12:30:00.000Z

Dieses „Round-trip“-Verhalten ist genau das, was du für APIs willst: konsistent, vorhersehbar und zeitzonenbewusst.

Parsing-Fallen: Date.parse und Browser-Unterschiede

Tests für DST-Grenzfälle hinzufügen
Lass Koder.ai Unit-Tests rund um DST-Grenzen und mehrere Zeitzonen erstellen.

Date.parse() wirkt bequem: gib ihm einen String, krieg einen Timestamp. Das Problem ist, dass für alles, was nicht eindeutig ISO 8601 ist, das Parsen auf Browser-Heuristiken beruht. Diese Heuristiken unterscheiden sich zwischen Engines und Versionen, was bedeutet, dass derselbe Input unterschiedlich (oder gar nicht) geparst werden kann, je nachdem, wo dein Code läuft.

Warum Date.parse() variieren kann

JavaScript standardisiert das Parsen zuverlässig nur für ISO 8601–artige Strings (und selbst da können Details wie Zeitzone eine Rolle spielen). Für „freundliche“ Formate — wie "03/04/2025", "March 4, 2025" oder "2025-3-4" — können Browser interpretieren:

  • Monat/Tag vs Tag/Monat basierend auf Locale-Annahmen
  • Fehlende Zeitzone als Ortszeit in einer Engine, aber verworfen in einer anderen
  • Leicht fehlerhafte Strings als „nah genug" … bis sie es nicht sind

Wenn du die genaue Form des Strings nicht vorhersagen kannst, kannst du das Ergebnis nicht vorhersagen.

Der überraschende Fall: YYYY-MM-DD

Eine verbreitete Falle ist die einfache Datumsform "YYYY-MM-DD" (z. B. "2025-01-15"). Viele Entwickler erwarten, dass sie als lokale Mitternacht interpretiert wird. In der Praxis behandeln einige Umgebungen diese Form jedoch als UTC-Mitternacht.

Dieser Unterschied ist wichtig: UTC-Mitternacht in Ortszeit umgerechnet kann zum vorherigen Tag in negativen Zeitzonen (z. B. Amerika) werden oder sich stündlich verschieben. So entstehen leicht Bugs wie „Warum weicht mein Datum um einen Tag ab?".

Parsing-Checkliste: Benutzereingabe vs Server-Input

Für Server/API-Input:

  • Bevorzuge vollständiges ISO 8601 mit expliziter Zeitzone, z. B. 2025-01-15T13:45:00Z oder 2025-01-15T13:45:00+02:00.
  • Behandle datums-only Werte als Daten, nicht als Moment in der Zeit. Wenn es ein Geburtstag oder Fälligkeitsdatum ist, halte es als plain String ("YYYY-MM-DD") und wandle es nicht in ein Date um, es sei denn, du definierst die beabsichtigte Zeitzone.

Für Benutzereingabe:

  • Akzeptiere keine mehrdeutigen Formate wie 03/04/2025, es sei denn, dein UI erzwingt die Bedeutung.
  • Bevorzuge kontrollierte Eingaben (Datepicker), die ein bekanntes Format liefern.
  • Wenn du freien Text parsen musst, definiere und erzwinge die akzeptierten Formate vorher.

Verwende explizite Regeln (keine Heuristiken)

Statt Date.parse() das „Herausfinden" zu überlassen, wähle eines dieser Muster:

  • Nur ISO 8601 vom Server akzeptieren; alles andere ablehnen.
  • Bekannte Formate manuell parsen (String splitten und new Date(year, monthIndex, day) für lokale Daten verwenden).
  • Timestamps (Epoch-Millisekunden) speichern und übertragen für präzise Instants und erst beim Anzeigen formatieren.

Wenn Zeitdaten kritisch sind, reicht „es funktioniert auf meinem Rechner" nicht — mache deine Parsing-Regeln explizit und konsistent.

Für Menschen formatieren mit Intl.DateTimeFormat

Wenn dein Ziel ist, „ein Datum/Zeit so anzuzeigen, wie Menschen es erwarten", ist das beste Werkzeug in JavaScript Intl.DateTimeFormat. Es nutzt die Locale-Regeln des Benutzers (Reihenfolge, Trennzeichen, Monatsnamen) und vermeidet das fragile manuelle Zusammenbauen von Strings wie month + '/' + day.

Warum es besser ist als manuelles String-Bauen

Manuelle Formatierung hardcoded oft US-Stil, vergisst führende Nullen oder produziert verwirrende 24/12-Stunden-Ergebnisse. Intl.DateTimeFormat macht außerdem explizit, in welcher Zeitzone du darstellst — wichtig, wenn deine Daten in UTC gespeichert sind, das UI aber die lokale Zeit widerspiegeln soll.

Gängige Optionen, die du tatsächlich benutzt

Für „schön formatieren" sind dateStyle und timeStyle am einfachsten:

const d = new Date('2025-01-05T16:30:00Z');

// Locale des Nutzers + lokale Zeitzone des Nutzers
console.log(new Intl.DateTimeFormat(undefined, {
  dateStyle: 'medium',
  timeStyle: 'short'
}).format(d));

// Erzwinge eine bestimmte Zeitzone (gut für Eventzeiten)
console.log(new Intl.DateTimeFormat('en-GB', {
  dateStyle: 'full',
  timeStyle: 'short',
  timeZone: 'UTC'
}).format(d));

Wenn du konsistente Stundenzyklen brauchst (z. B. Umschalter in den Einstellungen), benutze hour12:

console.log(new Intl.DateTimeFormat('en-US', {
  hour: 'numeric',
  minute: '2-digit',
  hour12: true
}).format(d));

Ein praktisches UI-Muster

Wähle eine Formatierungsfunktion pro „Typ" von Timestamp in deinem UI (Nachrichtenzeit, Logeintrag, Event-Start) und mache die timeZone-Entscheidung bewusst:

  • Verwende Ortszeit für „wann ist es für mich passiert".
  • Verwende eine feste Zone (oft UTC) für Audit-Logs, Server-Events oder teamübergreifende Koordination.

Das gibt konsistente, locale-freundliche Ausgaben ohne ein fragiles Set an Custom-Format-Strings.

Sommerzeit (DST): Der versteckte Ein-Stunden-Fehler

Logs überall vorhersehbar machen
Erzeuge UTC‑zentrische Logging- und Formatierungshilfen, die in Dev, CI und Produktion übereinstimmen.

Daylight Saving Time (DST) ist, wenn eine Zeitzone ihren UTC-Offset (typischerweise um eine Stunde) an bestimmten Daten ändert. Knifflig ist: DST ändert nicht nur den Offset — sie verändert die Existenz bestimmter lokalen Zeiten.

Fehlende und doppelte Wand-Uhr-Zeiten

Wenn die Uhr vorgestellt wird (spring forward), tritt ein Bereich lokaler Zeiten nicht auf. In vielen Regionen springt die Uhr z. B. von 01:59 direkt auf 03:00, sodass 02:30 lokale Zeit „fehlt".

Wenn die Uhr zurückgestellt wird (fall back), tritt ein Bereich lokaler Zeiten zweimal auf. Beispielsweise kann 01:30 einmal vor dem Wechsel und einmal danach auftreten — dieselbe Wand-Uhr-Zeit kann also zwei verschiedene Instants bedeuten.

24 Stunden hinzufügen vs „gleiche lokale Zeit morgen"

Das sind nicht dasselbe rund um DST-Grenzen:

  • 24 Stunden hinzufügen: „genau 24 * 60 * 60 Sekunden später"
  • Nächster Tag zur gleichen lokalen Zeit: „morgen um 9:00 AM in dieser Zeitzone"

Wenn heute Nacht DST beginnt, kann „morgen um 9:00" nur 23 Stunden entfernt sein. Wenn DST endet, kann es 25 Stunden sein.

// Scenario: schedule “same local time tomorrow”
const d = new Date(2025, 2, 8, 9, 0); // Mar 8, 9:00 local

const plus24h = new Date(d.getTime() + 24 * 60 * 60 * 1000);
const nextDaySameLocal = new Date(d);
nextDaySameLocal.setDate(d.getDate() + 1);

// Around DST, plus24h and nextDaySameLocal can differ by 1 hour.

Warum setHours überraschen kann

Wenn du so etwas machst wie date.setHours(2, 30, 0, 0) an einem „spring forward"-Tag, kann JavaScript das auf eine andere gültige Zeit normalisieren (oft 03:30), weil 02:30 in der lokalen Zeit nicht existiert.

Sicherere Ansätze

  • Führe Arithmetik in UTC für verstrichene Zeit (Dauern) durch: verwende Epoch-Millisekunden und UTC-Methoden.
  • Für lokale Planung, sei explizit über die Absicht: „nächster Tag um 9:00 lokal" sollte Kalenderoperationen (setDate) statt Millisekunden-Addition verwenden.
  • Beim Austausch über APIs: bevorzuge ISO 8601 mit Offset oder Z, damit das Instant eindeutig ist.

Dauern vs Daten: Verwende Date nicht für einen Timer

Eine häufige Fehlerquelle ist, Date zu benutzen, um etwas darzustellen, das kein Kalender-Moment ist.

Ein Timestamp beantwortet „wann ist das passiert?" (ein Instant wie 2025-12-23T10:00:00Z). Eine Dauer beantwortet „wie lange?" (z. B. „3 Minuten 12 Sekunden"). Das sind unterschiedliche Konzepte, und Vermischung führt zu verwirrender Rechnung und unerwarteten Zeitzonen-/DST-Effekten.

Warum Date das falsche Werkzeug für Dauern ist

Date repräsentiert immer einen Punkt auf der Zeitachse relativ zu einer Epoche. Wenn du „90 Sekunden" als Date speicherst, speicherst du effektiv „1970-01-01 plus 90 Sekunden" in einer bestimmten Zeitzone. Die Formatierung kann dann plötzlich 01:01:30 zeigen, sich um eine Stunde verschieben oder ein Datum anzeigen, das du nie wolltest.

Für Dauern bevorzuge einfache Zahlen:

  • Speichere Dauern als Sekunden oder Millisekunden (wähle eine Einheit und bleibe dabei).
  • Rechne mit Zahlen.
  • Konvertiere erst am Ende in einen Anzeigestring.

Sekunden in HH:mm:ss umwandeln

Ein einfacher Formatter für Countdown-Timer und Medienlängen:

function formatHMS(totalSeconds) {
  const s = Math.max(0, Math.floor(totalSeconds));
  const hh = String(Math.floor(s / 3600)).padStart(2, "0");
  const mm = String(Math.floor((s % 3600) / 60)).padStart(2, "0");
  const ss = String(s % 60).padStart(2, "0");
  return `${hh}:${mm}:${ss}`;
}

formatHMS(75);    // "00:01:15" (Countdown)
formatHMS(5423);  // "01:30:23" (Media-Dauer)

Wenn du von Minuten konvertierst, multipliziere zuerst (minutes * 60) und halte den Wert numerisch bis zur Ausgabe.

Vergleichen, Sortieren und Bereiche ohne Überraschungen

Wenn du Zeiten in JavaScript vergleichst, ist der sicherste Weg, Zahlen zu vergleichen, nicht formatierte Texte. Ein Date-Objekt ist letztlich ein Wrapper um einen numerischen Timestamp (Epoch-Millisekunden), also solltest du Vergleiche als „Zahl vs Zahl" abhandeln.

Sichere Vergleiche (Timestamps sind King)

Benutze getTime() (oder Date.valueOf(), das dieselbe Zahl zurückgibt), um zuverlässig zu vergleichen:

const a = new Date('2025-01-10T12:00:00Z');
const b = new Date('2025-01-10T12:00:01Z');

if (a.getTime() < b.getTime()) {
  // a ist früher
}

// funktioniert auch:
if (+a < +b) {
  // unary + ruft valueOf() auf
}

Vermeide den Vergleich formatierter Strings wie "1/10/2025, 12:00 PM" — die sind locale-abhängig und sortieren nicht korrekt. Die Ausnahme sind ISO 8601-Strings im selben Format und derselben Zeitzone (z. B. alle ...Z), die lexikographisch sortierbar sind.

Sortieren und Filtern nach Bereich

Sortieren nach Zeit ist einfach, wenn du nach Epoch-Millisekunden sortierst:

items.sort((x, y) => new Date(x.createdAt).getTime() - new Date(y.createdAt).getTime());

Filtern innerhalb eines Bereichs ist dasselbe:

const start = new Date('2025-01-01T00:00:00Z').getTime();
const end   = new Date('2025-02-01T00:00:00Z').getTime();

const inRange = items.filter(i => {
  const t = new Date(i.createdAt).getTime();
  return t >= start && t < end;
});

„Anfang/Ende des Tages" (lokal vs UTC)

„Anfang des Tages" hängt davon ab, ob du lokale Zeit oder UTC meinst:

// Lokaler Anfang/Ende des Tages
const d = new Date(2025, 0, 10); // 10. Jan in lokaler Zeit
const localStart = new Date(d.getFullYear(), d.getMonth(), d.getDate(), 0, 0, 0, 0);
const localEnd   = new Date(d.getFullYear(), d.getMonth(), d.getDate(), 23, 59, 59, 999);

// UTC Anfang/Ende des Tages
const utcStart = new Date(Date.UTC(2025, 0, 10, 0, 0, 0, 0));
const utcEnd   = new Date(Date.UTC(2025, 0, 10, 23, 59, 59, 999));

Wähle eine Definition früh und bleibe konsistent bei Vergleichen und Bereichslogik.

Debug-Checkliste: Wie man einen Zeit-Konversions-Bug diagnostiziert

Zeitstempel in allen Apps standardisieren
Erstelle gemeinsame Helfer für Sekunden vs Millisekunden, damit jeder Service dieselben Einheiten nutzt.

Zeitfehler wirken zufällig, bis du herausfindest was du hast (Timestamp? String? Date?) und wo die Verschiebung passiert (Parsing, Zeitzonen-Konversion, Formatierung).

1) Erfasse die „drei Sichten" desselben Moments

Logge denselben Wert auf drei verschiedene Weisen. Das zeigt schnell, ob das Problem Sekunden vs Millisekunden, Lokal vs UTC oder String-Parsing ist:

console.log('raw input:', input);

const d = new Date(input);
console.log('toISOString (UTC):', d.toISOString());
console.log('toString (local):', d.toString());
console.log('timezone offset (min):', d.getTimezoneOffset());

Worauf du achten solltest:

  • Wenn toISOString() stark daneben liegt (z. B. Jahr 1970 oder weit in der Zukunft), dann vermute Sekunden vs Millisekunden.
  • Wenn toISOString() korrekt aussieht, aber toString() verschoben ist, siehst du eine Ortszeit-Anzeige.
  • Wenn getTimezoneOffset() sich mit dem Datum ändert, überschreitest du Sommerzeit.

2) Prüfe die Umgebung: Zeitzone und Locale

Viele „funktioniert auf meinem Rechner"-Berichte sind einfach unterschiedliche Umgebungs-Defaults.

  • Browser: prüfe OS-Zeitzone und Browsersprache. Dann logge:
console.log(Intl.DateTimeFormat().resolvedOptions());
  • Node.js / Server: bestätige die Prozess-Zeitzone:
console.log('TZ:', process.env.TZ);
console.log(Intl.DateTimeFormat().resolvedOptions().timeZone);

Wenn dein Server in UTC läuft, aber dein Laptop in einer lokalen Zone, weichen formatierte Ausgaben ab, sofern du nicht explizit timeZone angibst.

3) Füge Tests dort hinzu, wo Zeit am häufigsten bricht

Erstelle Unittests rund um DST-Grenzen und „Edge“-Zeiten:

  • Eine Stunde vor und nach dem DST-Wechsel in relevanten Zonen
  • Monats-/Jahresende und 23:3000:30 Überläufe
  • Mehrere Zeitzonen, wenn dein Produkt sie unterstützt

Wenn du schnell iterierst, mache diese Tests zur Standard-Ausstattung. Zum Beispiel kannst du bei der Generierung eines React + Go-Apps in Koder.ai eine kleine „Zeit-Vertrag"-Test-Suite vorab anlegen (API-Payload-Beispiele + Parsing-/Formatierungs-Assertions), damit Regressionen vor Deployment auffallen.

Schnelle Pre-Ship-Checkliste

  • Inputs haben einen klaren Vertrag: Epoch-Millisekunden oder ISO 8601 mit Offset.
  • Keine mehrdeutigen Strings wie "2025-03-02 10:00".
  • Formatierung spezifiziert immer locale und (wenn nötig) timeZone.
  • Tests decken DST-Grenzen für deine Zielregionen ab.

Empfohlene Patterns für verlässliches Zeit-Handling

Verlässliches Zeit-Handling in JavaScript ist größtenteils eine Frage der Wahl einer „Quelle der Wahrheit" und der Konsistenz von Speicherung bis Anzeige.

Einfache Best Practices

Speichere und rechne in UTC. Betrachte benutzerseitige Lokalzeit als reine Präsentationsentscheidung.

Übertrage Daten zwischen Systemen als ISO 8601-Strings mit explizitem Offset (vorzugsweise Z). Wenn du numerische Epochs senden musst, dokumentiere die Einheit und halte sie konsistent (Millisekunden sind in JS üblich).

Formatiere für Menschen mit Intl.DateTimeFormat (oder toLocaleString) und übergebe eine explizite timeZone, wenn du deterministische Ausgabe brauchst (z. B. immer UTC oder eine spezifische Geschäftsregion).

Entscheidungsleitfaden: DB vs API vs UI

  • Datenbank: speichere ein Instant als UTC (Epoch-Millis oder ein UTC-Datetime-Typ). Vermeide „lokale" Datetimes, außer dein Domainmodell speichert tatsächlich Wand-Uhr-Zeiten (z. B. „Laden öffnet um 09:00").
  • API-Grenzen: bevorzuge ISO 8601 mit Z (z. B. 2025-12-23T10:15:00Z). Wenn du Epochs nutzt, benenne Felder wie createdAtMs, damit die Einheit klar ist.
  • UI: nimm das gespeicherte UTC-Instant und formatiere es für die Locale des Nutzers. Für Scheduling-UIs kennzeichne die Zeitzone deutlich und halte Konversionen explizit.

Wann sich eine Bibliothek lohnt

Ziehe eine dedizierte DateTime-Library in Betracht, wenn du wiederkehrende Events, komplexe Zeitzonenregeln, DST-sichere Arithmetik („gleiche lokale Zeit morgen") oder viel Parsing inkonsistenter Eingaben brauchst. Der Mehrwert liegt in klareren APIs und weniger Edge-Case-Bugs.

Wenn du tiefer einsteigen willst, schau dir mehr zeitbezogene Guides unter /blog an. Wenn du Tools oder Support evaluierst, siehe /pricing.

FAQ

Welches Format sollte ich für Zeitstempel in JavaScript verwenden?

Verwende Epoch-Millisekunden oder eine UTC-Zeichenfolge im ISO-8601-Format als internes Format. Wandle den Wert erst in ein lokales Format um, wenn du ihn einer Person anzeigst.

Warum wird mein Unix-Zeitstempel zu einem Datum im Jahr 1970?

JavaScript Date erwartet Millisekunden. Multipliziere einen Unix-Zeitstempel in Sekunden mit 1000, bevor du ihn an new Date() übergibst.

Warum zeigt dasselbe Datum auf verschiedenen Computern unterschiedliche Uhrzeiten an?

Ein Date speichert einen Zeitpunkt, doch JavaScript zeigt ihn oft in der lokalen Zeitzone des Computers an. Vergleiche toISOString() mit toString(), um festzustellen, ob sich die Daten geändert haben oder nur die Darstellung.

Wie verhindere ich, dass sich ein Datum um einen Tag verschiebt?

Speichere ein reines Datum wie einen Geburtstag oder ein Rechnungsdatum als YYYY-MM-DD. Wandle es erst in ein Date um, wenn du auch die gewünschte Zeitzone festlegst.

Welches Datumsformat ist für eine API am sichersten?

Sende einen vollständigen ISO-8601-Wert mit Z für UTC, etwa 2025-03-04T12:30:00Z, oder gib einen expliziten Offset an. Vermeide Zeichenfolgen wie 03/04/2025, weil Menschen und Browser sie unterschiedlich interpretieren können.

Wie sollte ich Daten für Nutzer formatieren?

Verwende Intl.DateTimeFormat und gib eine timeZone an, wenn die Anzeige eine feste Zeitzone benötigt. Es formatiert Daten entsprechend dem Gebietsschema der Nutzer, ohne dass du Zeichenfolgen manuell zusammensetzen musst.

Sollte ich `Date.parse` für von Nutzern eingegebene Daten verwenden?

Verlasse dich bei informellen oder mehrdeutigen Zeichenfolgen nicht darauf. Akzeptiere ein bekanntes ISO-Format von APIs oder parse kontrollierte Nutzereingaben mit Regeln, die du selbst festlegst.

Warum sollte ich `Date` nicht für einen Countdown oder eine Dauer verwenden?

Ein Date steht für einen Zeitpunkt im Kalender, eine Dauer dagegen nur für eine Zeitspanne. Speichere Dauern als Sekunden oder Millisekunden und formatiere die Zahl beim Ausgeben.

Wie wirkt sich die Sommerzeit auf JavaScript-Daten aus?

Verwende Epoch-Millisekunden für Berechnungen zur verstrichenen Zeit. Für Termine wie „morgen um 9:00 Uhr“ in einer benannten Zeitzone nutze kalenderbasierte Operationen und teste die Übergangsdaten zur Sommerzeit.

Wie lässt sich ein JavaScript-Zeitfehler am schnellsten debuggen?

Protokolliere die Rohdaten, toISOString(), toString() und getTimezoneOffset(). Ein falscher ISO-Wert weist oft auf ein Problem beim Parsen oder mit Einheiten hin, während eine abweichende lokale Zeichenfolge meist auf einen Unterschied bei der Zeitzonenanzeige hindeutet.

Related posts