W3docs

Java ZonedDateTime

Zeitzonenbezogene Datums- und Zeitangaben in Java mit ZonedDateTime und der Klasse ZoneId darstellen.

ZonedDateTime ist ein LocalDateTime mit einem angehängten ZoneId. Es sagt: „Dieses Kalenderdatum und diese Uhrzeit an diesem Ort." Die Kombination identifiziert einen einzelnen Moment auf der globalen Zeitlinie — 2025-11-04T14:00 [America/New_York] ist genau ein Instant, verschieden von 2025-11-04T14:00 [Europe/Berlin].

Dies ist die Klasse, auf die man zurückgreift, wenn die lokale Wanduhrzeit eines Ereignisses an einem bestimmten Ort wichtig ist. Besprechungskalender. Geplante cron-ähnliche Aufgaben, die um „9 Uhr in der Zeitzone des Benutzers" ausgelöst werden sollen. Alles, was eine Sommerzeit-Umstellung überstehen muss. LocalDateTime kennt nicht genug; Instant ist in UTC und trägt kein für Menschen bedeutungsvolles Zonenbezeichnung. ZonedDateTime ist beides.

ZoneId: der Katalog der Zeitzonen

Vor ZonedDateTime lernen wir ZoneId selbst kennen — eine Zeitzone wird durch eine ZoneId identifiziert, die man mit ZoneId.of(...) erhält:

ZoneId ny    = ZoneId.of("America/New_York");
ZoneId de    = ZoneId.of("Europe/Berlin");
ZoneId tokyo = ZoneId.of("Asia/Tokyo");
ZoneId utc   = ZoneId.of("UTC");
ZoneId sys   = ZoneId.systemDefault();

Die Zeichenketten sind IANA-Zeitzonendatenbank-Bezeichner (Region/Stadt). Die vollständige Liste ist ZoneId.getAvailableZoneIds() — etwa 600 Einträge, die regelmäßig aktualisiert werden, wenn Länder ihre Zeitzonen oder Sommerzeit-Regeln ändern. ZoneId trägt die historischen Aufzeichnungen der IANA, sodass Datumsangaben aus dem Jahr 1985 die damals geltenden Regeln verwenden.

Vermeiden Sie ZoneOffset (ein festes ±HH:MM), wenn Sie eine echte Zeitzone meinen. ZoneOffset.of("-05:00") ist für New York im November korrekt und im Juni falsch; ZoneId.of("America/New_York") ist das ganze Jahr über korrekt.

Dreibuchstabige Zeitzonennamen wie "EST" und "PST" sind mittlerweile größtenteils Aliase, mehrdeutig (war das Eastern Standard oder Eastern Australia?) und stillschweigend veraltet. Verwenden Sie Region/Stadt. "UTC" und "GMT" sind Sonderfälle und in Ordnung.

Erstellen

ZonedDateTime now    = ZonedDateTime.now();                                  // system zone
ZonedDateTime nowNY  = ZonedDateTime.now(ZoneId.of("America/New_York"));
ZonedDateTime made   = ZonedDateTime.of(2025, 11, 4, 14, 0, 0, 0, ZoneId.of("America/New_York"));
ZonedDateTime parsed = ZonedDateTime.parse("2025-11-04T14:00:00-05:00[America/New_York]");

Der häufigste Erstellungspfad lautet: „Ich habe ein LocalDateTime, ich habe eine ZoneId, verbinde sie":

LocalDateTime ldt = LocalDateTime.of(2025, 11, 4, 14, 0);
ZonedDateTime zdt = ldt.atZone(ZoneId.of("America/New_York"));

atZone(zone) ist die einzige Brückenmethode von einer lokalen Uhrzeit zu einem Zeitzonenmoment. Sie behandelt auch die zwei Sonderfälle, die die Sommerzeit mit sich bringt.

Sommerzeit: wenn die Wanduhr springt oder sich wiederholt

Zweimal im Jahr springt die Wanduhr in jeder Zone mit Sommerzeit oder wiederholt sich. Wenn sie vorgestellt wird — in den USA springt die Uhr am Sonntag im März von 02:00 auf 03:00 — existieren die Zeiten zwischen 02:00 und 03:00 an diesem Tag nicht. Wenn sie zurückgestellt wird, passieren die Zeiten zwischen 01:00 und 02:00 zweimal. ZonedDateTime muss in beiden Fällen etwas tun, und was es tut, ist dokumentiert:

  • Übersprungene Zeit (Lücke): atZone gibt die Zeit nach der Umstellung zurück. LocalDateTime.of(2025, 3, 9, 2, 30).atZone(ZoneId.of("America/New_York")) wird zu 03:30-04:00 — das JDK hat eine Stunde vorgerückt, um auf einer gültigen Wanduhrzeit zu landen.
  • Wiederholte Zeit (Überschneidung): atZone gibt den früheren der beiden gültigen Momente zurück (denjenigen vor der Offset-Änderung). Verwenden Sie withEarlierOffsetAtOverlap() oder withLaterOffsetAtOverlap(), um explizit zu wählen.
ZonedDateTime ambiguous = LocalDateTime.of(2025, 11, 2, 1, 30)
    .atZone(ZoneId.of("America/New_York"));                  // 01:30 EDT (earlier)
ZonedDateTime explicit = ambiguous.withLaterOffsetAtOverlap();   // 01:30 EST (later)

Die beiden ZonedDateTime-Objekte haben dasselbe LocalDateTime, aber unterschiedliche Offsets und unterschiedliche Instants. Dies ist der einzige Ort in java.time, an dem dieselbe lokale Uhrzeit legitimerweise auf zwei Momente abgebildet wird — und es ist die Quelle der Sommerzeit-bezogenen Fehler, von denen Sie gehört haben. Seien Sie bewusst, wenn Überschneidungen wichtig sind.

Zerlegung

ZoneId zone = zdt.getZone();
ZoneOffset offset = zdt.getOffset();
LocalDateTime ldt = zdt.toLocalDateTime();
LocalDate date = zdt.toLocalDate();
LocalTime time = zdt.toLocalTime();
Instant inst = zdt.toInstant();
OffsetDateTime odt = zdt.toOffsetDateTime();

Die Zugriffsmethoden lassen sich in drei Gruppen einteilen: die Zeitzonenhälfte (getZone, getOffset), die lokale Uhrzeithälfte (toLocalDateTime, toLocalDate, toLocalTime) und die globale Momenthälfte (toInstant). Alle drei sind gleichzeitig für dasselbe ZonedDateTime wahr; Sie wählen die benötigte Projektion.

OffsetDateTime ist ein verwandter Typ — LocalDateTime plus ein ZoneOffset (keine Zeitzone, keine Sommerzeit). Er ist nützlich für die Serialisierung von „2025-11-04T14:00-05:00" ohne Bindung an eine benannte Zone (oft das, was JSON-Zeitstempel wollen); für jeden Code, der sommerzeitbewusste Arithmetik benötigt, behalten Sie das ZonedDateTime.

Zwei Varianten von „nächster Tag"

ZonedDateTime hat zwei Methoden, die ähnlich aussehen, es aber nicht sind:

zdt.plusDays(1);                                              // add 1 day to the local clock reading
zdt.plus(Duration.ofHours(24));                                // add exactly 24 hours

An einem Sommerzeitumstellungstag divergieren die beiden. Am Tag, an dem die Uhren vorgestellt werden, landet plusDays(1) zur gleichen lokalen Zeit von morgen (die nur 23 Stunden realer Zeit entfernt ist). plus(Duration.ofHours(24)) landet eine Stunde später als gestern auf der Wanduhr.

ZielMethode
„Gleiche Zeit morgen" (kalendarisch)plusDays(1)
„Genau 24 Stunden von jetzt" (Dauer)plus(Duration.ofHours(24))

Beide sind korrekt; sie beantworten verschiedene Fragen. Wählen Sie bewusst.

Vergleiche und Gleichheit

zdt1.isBefore(zdt2);                                          // compares Instants
zdt1.isAfter(zdt2);
zdt1.isEqual(zdt2);                                           // compares Instants
zdt1.equals(zdt2);                                            // compares LocalDateTime + Zone + Offset

Der Unterschied ist deutlich:

  • isBefore/isAfter/isEqual vergleichen die zugrunde liegenden Momente (Instants).
  • equals vergleicht die vollständige Struktur — zwei ZonedDateTimes, die denselben Moment darstellen, aber unterschiedliche Zeitzonen haben, sind nicht equal.

Für „ist das derselbe Moment, unabhängig von der Zeitzone" verwenden Sie isEqual oder konvertieren Sie beide zu Instant und vergleichen Sie.

Ein praktisches Beispiel: eine Besprechung über drei Büros hinweg

Das folgende Programm plant eine Besprechung für 14:00 Berliner Zeit und berechnet, welche Zeit das in den Büros in New York und Tokio ist. Anschließend plant es eine wöchentlich wiederkehrende Besprechung, die eine Sommerzeitumstellung übersteht, und demonstriert den Unterschied zwischen plusDays(7) und plus(Duration.ofDays(7)) in einer Umstellungswoche.

java— editable, runs on the server

Was man aus dem Lauf mitnehmen sollte:

  • withZoneSameInstant(otherZone) ist die Operation für „Wie spät ist es in ihrem Büro?" — er hält den Moment fest und zeigt die Wanduhr in der neuen Zone an. Sein Geschwister withZoneSameLocal(otherZone) behält die Wanduhr bei und ändert den Moment (die Besprechung wird verschoben). Die Namen sind verwechselbar; der Unterschied liegt darin, welches Element gleich bleibt. Lesen Sie sie sorgfältig, wenn Sie sie schreiben.
  • berlin.equals(ny) war false, obwohl beide denselben Moment darstellten. equals vergleicht die vollständige Struktur (lokales Datum/Uhrzeit + Zone). Für „gleicher Moment, unabhängig davon, wie er bezeichnet ist", verwenden Sie isEqual oder vergleichen Sie Instants. Dies ist genau derselbe Unterschied, den LocalDate.equals vs. isEqual gemacht hat — equals für „gleicher Wertobjekt", isEqual für „gleicher Zeitpunkt".
  • Die Sommerzeitlücke (2025-03-09 02:30 in NY) wurde durch Vorspringen auf 03:30-04:00 aufgelöst. Das JDK hat keine Ausnahme geworfen; es hat den Moment nach der Umstellung gewählt. Wenn Sie unbedingt erkennen müssen, dass Sie eine unmögliche Zeit angegeben haben, verwenden Sie ZoneRules.getTransition(localDateTime) und prüfen Sie, ob das zurückgegebene Objekt eine Lücke ist.
  • Die Sommerzeitüberschneidung (2025-11-02 01:30 in NY) hat Ihnen zwei verschiedene ZonedDateTimes mit denselben lokalen Feldern und unterschiedlichen Offsets gegeben — EDT vs. EST, eine Stunde auseinander. withLaterOffsetAtOverlap() und withEarlierOffsetAtOverlap() sind die Methoden zur Auswahl. Wenn Sie geplante Ereignisse speichern, entscheiden Sie im Voraus, welches der Benutzer meint, und wenden Sie den richtigen Aufruf zur Analysezeit an.
  • plusDays(1) und plus(Duration.ofHours(24)) haben am Tag des Vorstellens der Uhren unterschiedliche Ergebnisse produziert — 23 Stunden realer Zeit im Vergleich zu 24 Stunden, mit unterschiedlichen lokalen Uhrzeiten. Verwenden Sie plusDays/plusWeeks für kalendarisch orientierte Planung („gleiche Zeit morgen") und plus(Duration) für die Berechnung vergangener Zeit („Alarm in 24 Stunden"). Die Wahl folgt fast immer der benutzerorientierten Absicht.

Was kommt als Nächstes

ZonedDateTime ist die menschenfreundliche Seite von „ein Moment mit einem Bezeichner". Das nächste Kapitel, Java Instant, ist die maschinenfreundliche Seite — ein Moment als Nanosekunden seit der Epoche, keine Zone, kein Kalender, der Typ, den jedes verteilte System auf der Leitung verwendet.

Übungen

Übung
Sie planen eine wiederkehrende Besprechung um 09:00 Uhr America/New_York und speichern den nächsten Termin mit `nextMeeting.plusDays(7)`. In der Woche, die die Sommerzeit-Umstellung (Vorstellung) überquert, was gilt für das Ergebnis?
Sie planen eine wiederkehrende Besprechung um 09:00 Uhr America/New_York und speichern den nächsten Termin mit `nextMeeting.plusDays(7)`. In der Woche, die die Sommerzeit-Umstellung (Vorstellung) überquert, was gilt für das Ergebnis?
Was this page helpful?