Startseite / Artikel

Abstimmung von zeitlichen Typen mit tatsächlichen Datenaufgaben: Sieben bearbeitete Beispiele

Arbeiten Sie mit JavaScript Temporal an sieben alltäglichen Aufgaben zu Datums- und Zeitangaben – von einfachen Datumsformaten und Monatsüberschreitungen bis hin zu Zeitzonen, DST-Rechnungen und Validierung.

Verlag: REACTAPP.TOP9 Min. Lesezeit2060 Wörter

Quellen: JavaScript Temporal: 7 Date and Time Problems Explained Simply

Die Anforderungen an Datumsverarbeitung klingen in der Regel trivial: Ein Abo um einen Monat verlängern, ein Treffen im Zeitzone eines Kollegen anzeigen oder die Tage zwischen zwei Datumsangaben zählen. Die Komplikationen treten später auf – wenn sich herausstellt, dass Februar zu kurz ist, eine Sommerzeitumstellung dazu führt, dass „morgen“ 23 Stunden entfernt ist, oder ein Geburtstag gespeichert werden muss, obwohl er überhaupt keine Zeitzone hat. Die Temporal-API von JavaScript löst dieses Problem, indem sie jeder Wertart ihren eigenen Typ zuweist; der Typ selbst dokumentiert somit, was der Wert bedeutet. Die sieben untenstehenden Beispiele zeigen, welcher Typ für welche Aufgabe geeignet ist, wo die Standardwerte überraschen können und welche Entscheidungen Temporal absichtlich Ihrer Anwendung überlässt.

Einrichtung eines Umfelds zum Ausführen der Beispiele

Die native Unterstützung wird noch implementiert. Zum Zeitpunkt der Erstellung listet MDN Temporal als eingeschränkt verfügbar auf, daher sollten Sie die Unterstützung in Ihren Zielbrowsern sowie der Node.js-Version überprüfen, bevor Sie sich auf die eingebauten Implementierungen verlassen. MDNs Referenzseite zu Temporal zeigt den aktuellen Kompatibilitätsstatus an.

Für Experimente ohne native Unterstützung installieren Sie den Temporal Polyfill in einen leeren Projektordner. Dafür benötigen Sie Node.js und npm:

mkdir temporal-demo
cd temporal-demo
npm init -y
npm install @js-temporal/polyfill@0.5.1

Erstellen Sie eine Datei mit dem Namen demo.mjs und beginnen Sie sie mit diesem Import:

import { Temporal } from "@js-temporal/polyfill";

Fügen Sie eines der untenstehenden Beispiele unter den Import ein und führen Sie die Datei aus:

node demo.mjs

Sämtliche Beispiele gehen von derselben Import-Methode aus. Der Polyfill exportiert eine lokale Temporal-Binding und installiert keine globale, weshalb jedes Datei, das ihn verwendet, diesen Import benötigt. Die Version ist auf 0.5.1 festgelegt, da dies die Version ist, für die die Beispiele geschrieben wurden; prüfen Sie vor der Verwendung in einem echten Projekt die veröffentlichten Versionen des Polyfills.

1. Ein Kalendertag ohne Zeit oder Zeitzone speichern

Ein Geburtstag oder ein Vertragsabgabetermin hat oft keine bedeutungsvolle Tageszeit. Wenn die Anforderung lediglich „15. Oktober“ lautet, führt das Hinzufügen von Mitternacht und einer Zeitzone zur Erfindung von Informationen – genau diese erdachten Informationen sind es, die später dazu führen, dass der Tag bei der Anzeige an anderer Stelle um einen Tag verschoben wird. Temporal.PlainDate repräsentiert lediglich den Tag:

const deadline = Temporal.PlainDate.from("2026-10-15");

Seine Felder sind einfache Zahlen und es wird wieder in einen ISO-String serialisiert:

console.log(deadline.year);       // 2026
console.log(deadline.month);      // 10
console.log(deadline.day);        // 15
console.log(deadline.toString()); // 2026-10-15

Der Wert weist weder Uhrzeit noch UTC-Offset noch Zeitzone auf. Standardmäßig wird das ISO-Kalenderformat verwendet, und im Gegensatz zum herkömmlichen Date-Objekt sind die Monate mit 1 bis 12 nummeriert anstatt bei null zu beginnen.

Bedenken Sie diese Einschränkung: Ein PlainDate gibt keinen absoluten Zeitpunkt an. Wenn der Fristzeitpunkt tatsächlich „17 Uhr in Delhi“ bedeutet, benötigen Sie auch eine Zeitangabe und eine Zeitzone – genau dafür dient ZonedDateTime.

2. Neue Datumsangaben ableiten, ohne das Original zu verändern

Gemeinsam genutzte, veränderliche Datumsangaben sind eine klassische Quelle für Fehler: Ein Teil des Codes ändert einen Wert, den ein anderer Teil noch liest. Temporale Objekte sind unveränderlich, und arithmetische Operationen erzeugen stets ein neues Objekt:

const originalDate = Temporal.PlainDate.from("2026-10-15");
const extendedDate = originalDate.add({ days: 7 });

Durch das Protokollieren beider Werte wird deutlich, dass das Original unverändert bleibt:

console.log(originalDate.toString()); // 2026-10-15
console.log(extendedDate.toString()); // 2026-10-22

Das bedeutet, dass Sie den ursprünglichen Fristtermin und den verlängerten Termin nebeneinander beibehalten können, ohne zunächst etwas klonen zu müssen.

Auf der anderen Seite gibt es einen häufigen Fehler: Man ruft originalDate.add({ days: 7 }) in einer eigenen Zeile auf und erwartet, dass sich originalDate ändert. Das geschieht jedoch nicht. Weisen Sie das Ergebnis zu oder geben Sie es zurück.

3. Bestimmen Sie, was „ein Monat später“ bedeutet

Das Hinzufügen eines Monats zum 31. Januar hat keine einzige richtige Lösung, denn der 31. Februar existiert nicht. Ihre Anwendung benötigt eine Regel. Temporal beschränkt standardmäßig das Ergebnis auf den nächstgelegenen gültigen Datumszeitpunkt. Beginnen Sie mit dem Ende des Januars:

const januaryEnd = Temporal.PlainDate.from("2026-01-31");

Das Hinzufügen eines Monats begrenzt den Tag auf das Ende des Februars:

const nextMonth = januaryEnd.add({ months: 1 });console.log(nextMonth.toString()); // 2026-02-28

Wenn ein stilles Verschieben des Tages inakzeptabel ist, sollte man Temporal anweisen, stattdessen einen Fehler auszulösen, indem overflow: „reject“ übergeben wird. Am Beispiel eines Rechnungsdatums:

const billingDate = Temporal.PlainDate.from("2026-01-31");

Die Ablehnung zeigt sich als RangeError, den der Codeausschnitt abfängt und anschließend alle anderen Fehler erneut auslöst:

try {
  billingDate.add({ months: 1 }, { overflow: "reject" });
} catch (error) {
  if (!(error instanceof RangeError)) throw error;
  console.log("That day does not exist in the next month.");
}

Keine der Optionen entspricht direkt der Rechnungspolitik; jede dient lediglich als Mechanismus zur Umsetzung dieser. Zudem gibt es einen kumulativen Effekt zu beachten: Wenn der 31. Januar auf den 28. Februar begrenzt wird, führt das Hinzufügen eines weiteren Monats zum 28. März und nicht zum 31. März. Somit driftet ein Zeitplan, der durch wiederholtes Hinzufügen eines Monats zum vorherigen Wert erstellt wird, im Laufe der Zeit ab. Wenn die Regel „der letzte Tag jedes Monats“ lautet, sollte diese Regel explizit modelliert werden – beispielsweise indem stets von dem ursprünglichen Referenzdatum ausgegangen wird oder die Länge des Monats berücksichtigt wird, anstatt fortwährend Monate hinzuzufügen.

4. Anzeige eines Moments in mehreren Zeitzonen

Ein Zeitstempel für ein Ereignis bezeichnet einen genauen Moment. Das lokale Datum und die Uhrzeit auf dem Bildschirm einer Person hängen davon ab, wo sie sich befindet. Temporal.Instant repräsentiert den Moment selbst:

const meeting = Temporal.Instant.from("2026-10-15T09:00:00Z");

Durch Umrechnung in benannte Zeitzonen erhält man die lokale Uhrzeit an jedem Ort, und durch Umrechnung zurück zeigt sich, dass beide weiterhin auf denselben Moment verweisen:

const delhi = meeting.toZonedDateTimeISO("Asia/Kolkata");
const london = meeting.toZonedDateTimeISO("Europe/London");console.log(delhi.toString());
// 2026-10-15T14:30:00+05:30[Asia/Kolkata]console.log(london.toString());
// 2026-10-15T10:00:00+01:00[Europe/London]console.log(delhi.toInstant().equals(london.toInstant()));
// true

Die lokalen Darstellungen unterscheiden sich, doch der zugrunde liegende Moment ist identisch. (Die zusammengeführten Zeilen in diesem Auszug sollten jeweils als eigene Anweisung betrachtet werden.)

Verwenden Sie benannte Zeitzonen wie Europe/London anstelle fester Offsetwerte. Eine benannte Zeitzone enthält die Regeln dafür, wie sich ihr Offset im Laufe des Jahres ändert; ein fest codierter +01:00 beschreibt nur London während der Sommerzeit.

Hier beginnen viele Fehler: Ein Zeitunterschied ist keine Zeitzone. Ein Zeitunterschied gibt den Unterschied zu UTC zu einem bestimmten Moment an, während eine benannte Zone die Regeln liefert, die diesen Unterschied zu jedem beliebigen Zeitpunkt bestimmen.

Die obigen Zeichenketten sind maschinenorientiert, einschließlich der in Klammern stehenden Zoneangabe. Für alles, was den Benutzern angezeigt wird, sollte der Wert entsprechend ihrer Lokalisation formatiert werden – beispielsweise mit toLocaleString() oder Intl.DateTimeFormat – anstatt die rohe Zeichenkette auszugeben.

5. „Morgen“ und „in 24 Stunden“ getrennt halten

Im Großen und Ganzen liefern diese Ausdrücke das gleiche Ergebnis das ganze Jahr über. Um die Zeitumstellung geht das jedoch nicht auf. Nehmen wir Mittag in New York am 7. März 2026, dem Tag vor der Uhrumstellung:

const beforeClockChange = Temporal.ZonedDateTime.from(
  "2026-03-07T12:00:00-05:00[America/New_York]"
);

Das Hinzufügen eines Kalendertages und das Hinzufügen von 24 Stunden ergeben unterschiedliche örtliche Zeiten:

const tomorrow = beforeClockChange.add({ days: 1 });
const after24Hours = beforeClockChange.add({ hours: 24 });console.log(tomorrow.toString());
// 2026-03-08T12:00:00-04:00[America/New_York]console.log(after24Hours.toString());
// 2026-03-08T13:00:00-04:00[America/New_York]

Die Kalenderarithmetik hält die Uhrzeit am Mittag bei. Die Zeitdauerberechnung zählt genau 24 Stunden, und da die Uhr über Nacht eine Stunde vorgeschaltet wurde, liegt das Ergebnis bei 13 Uhr.

Jede Methode eignet sich für unterschiedliche Anforderungen. Ein täglicher Termin bedeutet in der Regel „dieselbe örtliche Zeit morgen“, daher sollten Tage hinzugefügt werden. Ein Token oder eine Reservierung, die genau nach 24 Stunden abläuft, sollte Stunden berücksichtigen. Temporal kann beides berechnen; man muss entscheiden, welche Bedeutung die Funktion benötigt, und es lohnt sich, diese Entscheidung aufzuschreiben.

6. Ungültige Datumsangaben und nicht existierende örtliche Zeiten ablehnen

Beim Validieren von Formulareingaben sollte ein unmögliches Datum einen Fehler verursachen, anstatt stillschweigend in ein anderes Datum umgewandelt zu werden. Für Objekteingaben sollte eine strenge Handhabung mit overflow: „reject“ angefordert werden:

try {
  Temporal.PlainDate.from(
    { year: 2026, month: 2, day: 30 },
    { overflow: "reject" }
  );
} catch (error) {
  if (!(error instanceof RangeError)) throw error;
  console.log("Please choose a valid calendar date.");
}

Ohne diese Option wäre das Objekt { year: 2026, month: 2, day: 30 } auf den 28. Februar beschränkt. ISO-Strings verhalten sich anders: Die Parsung von "2026-02-30" wird direkt abgelehnt, anstatt korrigiert zu werden. Man sollte nicht annehmen, dass die String-Parsing- und Objektkonstruktionsfunktionen das gleiche Überlaufverhalten aufweisen.

Ein gültiges Datum kann dennoch mit einer örtlichen Zeit kombiniert werden, die niemals auftritt. Wenn New York am 8. März 2026 seine Uhren vorstellt, wird die Zeit 2:30 Uhr morgens vollständig übersprungen. Die Option disambiguation steuert, was mit solchen Eingaben geschieht:

try {
  Temporal.ZonedDateTime.from(
    "2026-03-08T02:30[America/New_York]",
    { disambiguation: "reject" }
  );
} catch (error) {
  if (!(error instanceof RangeError)) throw error;
  console.log("Choose a time that exists and is unambiguous.");
}

Mit disambiguation: „reject“ wirft Temporal sowohl bei übersprungenen als auch bei wiederholten Zeiten Fehler aus – insbesondere dann, wenn eine Stunde zweimal auftritt, wenn die Uhren zurückgestellt werden. Die anderen Einstellungen wählen automatisch eine Interpretation aus. Bei einem Buchungsformular ist es in der Regel klarer, den Benutzer zu bitten, eine gültige Zeit auszuwählen, anstatt seinen Termin ohne Vorankündigung zu verschieben.

7. Datumsvergleiche, -messungen und -sortierungen explizit durchführen

Für chronologische Vergleiche sollten die eigenen Methoden des Datentyps verwendet werden. Beginnen Sie mit zwei Datumsangaben:

const startDate = Temporal.PlainDate.from("2026-10-15");
const endDate = Temporal.PlainDate.from("2026-10-20");

Temporal.PlainDate.compare() gibt -1, 0 oder 1 zurück, je nachdem ob der erste Argument früher, gleich oder später ist. until() gibt einen Temporal.Duration zurück, der im ISO 8601-Dauerformat dargestellt wird – fünf Tage erscheinen beispielsweise als P5D:

console.log(Temporal.PlainDate.compare(startDate, endDate));
// -1const difference = startDate.until(endDate, {
  largestUnit: "days",
});console.log(difference.days);       // 5
console.log(difference.toString()); // P5D

Für PlainDate wird der Unterschied in Kalendertagen gemessen. Es werden keine verstrichenen Stunden in einem bestimmten Zeitzone berechnet, und es wird auch nicht entschieden, ob Ihr Produkt den Start- und Endtag mitzählt; diese „One-Off“-Regel müssen Sie selbst definieren.

Weil compare die Signatur aufweist, die Array.prototype.sort erwartet, kann es direkt übergeben werden. Gegeben ist eine unsortierte Liste von Datumsangaben:

const dates = [
  "2026-11-03",
  "2026-10-15",
  "2026-10-20",
].map(value => Temporal.PlainDate.from(value));

Die Sortierung lässt sich somit in einer einzigen Zeile ausführen:

dates.sort(Temporal.PlainDate.compare);console.log(dates.map(date => date.toString()).join(", "));
// 2026-10-15, 2026-10-20, 2026-11-03

Vermeiden Sie Gewohnheiten, die aus Date übernommen wurden. Das Subtrahieren von Temporal-Objekten oder das Vergleichen derselben mit < und > führt bei diesen Typen zu Fehlern – was absichtlich so ist: Es verhindert zufällige Vergleiche der falschen Art. === prüft hingegen die Objektidentität, weshalb zwei getrennt erstellte gleichwertige Datumsangaben nicht === sind; verwenden Sie stattdessen .equals() für den Wertgleichheitstest. Weitere subtilere Möglichkeiten, wie alte Gewohnheiten wieder zum Vorschein kommen können, finden Sie in unserem Leitfaden zu Temporal-API- Fallen, die Datumsfehler erneut verursachen.

Wählen Sie den Typ vor dem Ausführen der Berechnung

Die meisten Datumsfehler sind in Wirklichkeit Modellierungsfehler – klären Sie daher zunächst, was ein Wert bedeutet:

  • Ein Datum ohne Zeit oder Zeitzone: PlainDate
  • Eine Uhrzeit ohne Datum oder Zeitzone: PlainTime
  • Eine lokale Datum und Uhrzeit ohne Zeitzone: PlainDateTime
  • Ein genauer Punkt auf der globalen Zeitlinie: Instant
  • Eine Datum und Uhrzeit, die mit einer benannten Zeitzone verknüpft ist: ZonedDateTime
  • Eine Menge an Kalender- oder Uhrenzeit: Duration
  • Diese Wahl erfordert eine Besprechung, wenn Sie einen API-Vertrag oder eine Datenbankspalte entwerfen. Ein Feld namens einfach date führt zu Unstimmigkeiten darüber, ob es ein Kalenderdatum, einen UTC-Zeitstempel oder eine lokale Zeit enthält.

    Die Migration muss nicht auf einmal erfolgen. Wählen Sie eine Funktion mit klaren Anforderungen aus, definieren Sie deren Eingabeformat, den Temporal-Typ, die Zeitzone-Strategie sowie die erwartete Ausgabe und fügen Sie Testfälle für Monatsende und Uhrenumstellungen hinzu, wo dies relevant ist. Den Datencode für das Arbeitsdatum kann man unverändert lassen, bis es einen Grund gibt, ihn anzufassen.

    Prülliste vor der Produktion

    • Überprüfen Sie die Unterstützung für temporale Daten in jedem Umfeld, in das Sie Ihre Anwendung liefern, und berücksichtigen Sie bei Verwendung eines Polyfills dessen Download- sowie Parse-Kosten in Ihrem Budget für die Dateibündel.
    • Klären Sie mit Ihrem Backend die Serienisierungsregeln ab. Ein einfacher Datumswert, ein UTC-Zeitpunkt sowie eine Zeichenkette mit einem eckigen Klammern umschlossenen Zeitzone-Identifikator sind drei verschiedene Formate.
    • Machen Sie die Geschäftsregeln explizit: Darf „ein Monat später“ den Tag verändern, sollten unklare Terminzeiten abgelehnt werden, und bezieht sich eine Dauer auf Kalendertage oder vergangene Stunden?

    Zusammenfassung

    Der Hauptbeitrag von Temporal besteht nicht darin, eine benutzerfreundlichere API-Oberfläche zu bieten, sondern darin, Unterschiede hervorzuheben, die von Date verschleiert wurden: Datumsangaben gegenüber Momenten, Zeitverschiebungen gegenüber Zeitzonen sowie Kalenderarithmetik gegenüber vergangener Zeit. Es stellt präzise Werkzeuge für jeden Fall bereit, einschließlich strenger Modi, die deutlich Fehler melden anstelle von Vermutungen anzustellen. Was es jedoch nicht kann, ist, Ihre Richtlinien für Sie zu wählen. Entscheiden Sie diese Regeln bewusst, kodieren Sie sie in den Typen und Optionen, die Sie übergeben, und behandeln Sie Randfälle mit Tests.