ZonedDateTime — was es ist, Arbeit mit Zeitzonen und Zeit

Autor: IT Sectr Veröffentlicht: 2026-07-13 Lesezeit: 10 Min.

ZonedDateTime ist eine unveränderliche (immutable) Klasse aus dem java.time-Paket, die Datum und Uhrzeit zusammen mit Zeitzoneninformationen (ZoneId) speichert. Anders als LocalDateTime identifiziert ZonedDateTime eindeutig einen Moment auf der Zeitachse. Laut der Spezifikation von Oracle Java 17 (2024) behandelt die Klasse Sommerzeitumstellungen (DST) korrekt durch die Zonenregeln der IANA Time Zone Database.

Wichtige Punkte

  • ZonedDateTime ist eine unveränderliche Klasse, die Datum, Uhrzeit und Zeitzone (ZoneId) in einem einzigen Objekt vereint.
  • Anders als LocalDateTime definiert ZonedDateTime eindeutig einen Moment auf der Zeitachse und eignet sich für globale Systeme.
  • Die Klasse behandelt automatisch Sommerzeitumstellungen (DST) gemäß den Regeln der IANA Time Zone Database.
  • Zur Konvertierung zwischen Zeitzonen wird die Methode withZoneSameInstant(ZoneId) verwendet.
  • Die Speicherung von ZonedDateTime in Datenbanken wird über OffsetDateTime oder TIMESTAMP WITH TIME ZONE empfohlen.

Was ist ZonedDateTime?

ZonedDateTime ist eine der wichtigsten Klassen im java.time-Paket, die Datum und Uhrzeit mit vollständigen Zeitzoneninformationen darstellt. Sie kombiniert drei Komponenten: LocalDateTime (Datum und Uhrzeit), ZoneId (Zonenkennung) und ZoneOffset (Offset relativ zur UTC).

Anders als LocalDateTime, das nur die Wanduhrzeit (wall-clock time) ohne Zeitzonenbindung speichert, identifiziert ZonedDateTime eindeutig einen Moment. Zwei identische LocalDateTime-Instanzen in verschiedenen Zeitzonen repräsentieren unterschiedliche Zeitpunkte. Zwei identische ZonedDateTime-Instanzen repräsentieren denselben Moment.

Die Klasse ist vollständig unveränderlich und thread-safe. Alle arithmetischen Operationen geben ein neues Objekt zurück. ZonedDateTime implementiert das ChronoZonedDateTime-Interface und kann überall dort verwendet werden, wo in Java mit Zonenzeiten gearbeitet wird.

Laut den Oracle Java 17 Spezifikationen unterstützt ZonedDateTime die Arbeit mit jeder Zone aus der IANA Time Zone Database, die über 600 Zeitzonen umfasst.

ZonedDateTime vs LocalDateTime: Was ist der Unterschied?

Der Hauptunterschied — ZonedDateTime enthält eine Zeitzone, während LocalDateTime keine enthält. Dieser grundlegende Unterschied bestimmt den Anwendungsbereich jeder Klasse.

LocalDateTime wird für lokale Ereignisse verwendet: Konzertzeit, Unterrichtsplan, Geburtsdatum. Wenn ein Ereignis in Moskau um 15:00 Uhr stattfindet, zeichnet LocalDateTime 15:00 ohne Bindung auf. Wenn Sie den Server nach New York verschieben, bleibt die Zeit 15:00 — aber es wird ein anderer physikalischer Moment sein.

ZonedDateTime wird für globale Daten verwendet: Server-Logs, API-Zeitstempel, internationale Meetings. Wenn ein Meeting um 15:00 MSK geplant ist, bewahrt ZonedDateTime sowohl die Zeit als auch die Zone. In New York wird es korrekt als 8:00 EST angezeigt. Laut Baeldung (2024) ist die Wahl zwischen LocalDateTime und ZonedDateTime die häufigste architektonische Entscheidung bei der Arbeit mit Daten.

Faustregel: Wenn Daten für eine einzelne Region gespeichert werden — verwenden Sie LocalDateTime. Wenn Daten Zeitzonengrenzen überschreiten — verwenden Sie ZonedDateTime. Wenn Sie einen absoluten Moment darstellen müssen — verwenden Sie Instant.

Wie funktioniert die Zeitzone in java.time?

Die Zeitzone in java.time wird durch die Klasse ZoneId repräsentiert. ZoneId ist eine Zonenkennung im Format „Kontinent/Region“, zum Beispiel „Europe/Moscow“, „America/New_York“, „Asia/Tokyo“. ZoneId wird über die statische Methode of(String zoneId) oder über die Standardzeitzone des Systems ermittelt.

ZoneId wird in zwei Typen unterteilt: fixed offset (fester Offset, z.B. „+03:00“) und region-based (regionale Zonen, z.B. „Europe/London“). Regionale Zonen enthalten Sommerzeitumstellungsregeln und historische Änderungen. Fixed offset ist lediglich ein fester Offset.

Um den aktuellen Offset einer ZoneId zu einem bestimmten Zeitpunkt zu erhalten, verwenden Sie die Methode getRules(), die ZoneRules zurückgibt. ZoneRules enthält alle Übergänge und Offsets für eine bestimmte Zone. Dies ist der Schlüsselmechanismus für die korrekte DST-Behandlung.

Alle Zeitzonen werden mit dem JDK über tzdata-Dateien (IANA Time Zone Database) ausgeliefert und regelmäßig aktualisiert. Unter Android hängt die tzdata-Version von Systemaktualisierungen über Google Play Services ab.

Erstellen von ZonedDateTime

ZonedDateTime kann auf verschiedene Weisen erstellt werden. Die einfachste ist now(), die die aktuelle Zeit in der Systemzeitzone zurückgibt. Die Variante now(ZoneId) ermöglicht es, die aktuelle Zeit in einer bestimmten Zone zu erhalten.

Die Methode of(LocalDateTime, ZoneId) erstellt eine ZonedDateTime aus lokaler Zeit und Zone. Die Variante of(int year, int month, int dayOfMonth, int hour, int minute, int second, int nanoOfSecond, ZoneId zone) erstellt aus Komponenten.

LocalDateTime kann über die Methode atZone(ZoneId) in ZonedDateTime konvertiert werden. Instant — über Instant.atZone(ZoneId). Date — über Date.toInstant().atZone(ZoneId).

kotlin
val moscowZone = ZoneId.of("Europe/Moscow")

val nowInMoscow = ZonedDateTime.now(moscowZone)

val fromComponents = ZonedDateTime.of(
    2026, 7, 21, 15, 30, 0, 0, moscowZone
)

val fromLocal = LocalDateTime.now().atZone(moscowZone)

val fromInstant = Instant.now().atZone(moscowZone)

Konvertierung zwischen Zeitzonen

Die wichtigste Konvertierungsmethode ist withZoneSameInstant(ZoneId). Sie konvertiert eine ZonedDateTime in eine andere Zeitzone, während derselbe Moment erhalten bleibt. Zum Beispiel 15:00 MSK → 8:00 EST. Die Methode withZoneSameLocal(ZoneId) ändert die Zone, während die lokale Zeit erhalten bleibt — dies ergibt einen anderen Moment.

Um den Offset relativ zur UTC zu erhalten, verwenden Sie die Methode getOffset(), die ZoneOffset zurückgibt. ZoneOffset ist eine Unterklasse von ZoneId, die einen festen Offset im Format „+HH:mm“ oder „-HH:mm“ darstellt.

Die Konvertierung in Instant erfolgt über die Methode toInstant(). Instant ist ein absoluter Zeitpunkt, unabhängig von der Zeitzone. Die umgekehrte Konvertierung ist Instant.atZone(ZoneId).

kotlin
val moscow = ZonedDateTime.of(
    2026, 7, 21, 15, 0, 0, 0,
    ZoneId.of("Europe/Moscow")
)

val newYork = moscow.withZoneSameInstant(
    ZoneId.of("America/New_York")
)

val utcInstant = moscow.toInstant()
val backToMoscow = utcInstant.atZone(ZoneId.of("Europe/Moscow"))

Arbeiten mit der Sommerzeit (DST)

Sommerzeitumstellungen verursachen zwei Probleme: Lücken (gaps) und Überlappungen (overlaps). Eine Lücke entsteht im Frühjahr, wenn die Uhren vorgestellt werden — eine bestimmte Zeit existiert nicht. Eine Überlappung entsteht im Herbst, wenn die Uhren zurückgestellt werden — dieselbe Zeit tritt zweimal auf.

ZonedDateTime behandelt diese Situationen durch eine Auflösungsstrategie. Beim Erstellen eines Objekts während einer Lücke verschiebt java.time die Zeit automatisch um den Offset-Betrag. Beim Erstellen während einer Überlappung wird die erste Option (vor der Umstellung) ausgewählt. Dieses Verhalten kann über withZoneSameInstant geändert werden.

Sie können über zone.getRules().isDaylightSavings(instant) prüfen, ob eine Zeit in der Sommerzeit liegt. Die Methode getOffset() zeigt den tatsächlichen Offset für einen bestimmten Moment an, und getRules().getDaylightSavings(instant) zeigt den DST-Anpassungsbetrag in Millisekunden.

kotlin
fun checkDST(zdt: ZonedDateTime) {
    val rules = zdt.getZone().getRules()
    val instant = zdt.toInstant()

    if (rules.isDaylightSavings(instant)) {
        val dstAmount = rules.getDaylightSavings(instant)
        Log.d("Sommerzeit", "Sommerzeit-Offset: $dstAmount")
    }
}

Formatierung von ZonedDateTime

Zur Formatierung von ZonedDateTime verwenden Sie DateTimeFormatter. Das Standard-ISO-Format enthält Datum, Uhrzeit und Offset: „2026-07-21T15:30:00+03:00[Europe/Moscow]“. Vordefinierte Formate: ISO_ZONED_DATE_TIME, ISO_OFFSET_DATE_TIME, ISO_INSTANT.

Für lokalisierte Ausgabe verwenden Sie DateTimeFormatter.ofLocalizedDateTime(FormatStyle). FormatStyle kann SHORT, MEDIUM, LONG, FULL sein. LONG enthält den Zonennamen (“MSK“), FULL enthält den vollständigen Namen („Moscow Standard Time“).

Wichtig: Beim Parsen eines Strings mit ZonedDateTime muss das Format Zonen- oder Offset-Informationen enthalten. Wenn die Zone nicht angegeben ist, verwenden Sie LocalDateTime.parse() und dann atZone().

kotlin
val zdt = ZonedDateTime.now(ZoneId.of("Europe/Moscow"))

val iso = zdt.format(DateTimeFormatter.ISO_ZONED_DATE_TIME)

val custom = DateTimeFormatter
    .ofPattern("dd.MM.yyyy HH:mm z")
val formatted = zdt.format(custom)

val parsed = ZonedDateTime.parse(
    "2026-07-21T15:30:00+03:00",
    DateTimeFormatter.ISO_OFFSET_DATE_TIME
)

ZonedDateTime in Android: Praxisbeispiele

Das erste Beispiel — Anzeige der Besprechungszeit für den Benutzer in seiner lokalen Zeitzone. Der Server sendet ZonedDateTime in UTC, der Client konvertiert in die lokale Zeitzone des Geräts.

kotlin
fun displayMeetingTime(
    serverUtc: ZonedDateTime
): String {
    val deviceZone = ZoneId.systemDefault()
    val localTime = serverUtc.withZoneSameInstant(deviceZone)
    val formatter = DateTimeFormatter
        .ofPattern("dd.MM.yyyy HH:mm z")
    return localTime.format(formatter)
}

Das zweite Beispiel — Berechnung der Zeit bis zum nächsten Ereignis unter Berücksichtigung der Zeitzone. Wir verwenden ZonedDateTime für die Serverzeit und Duration.between() zur Berechnung der Differenz.

kotlin
fun timeUntilEvent(eventTime: ZonedDateTime): String {
    val now = ZonedDateTime.now()
    val duration = Duration.between(now, eventTime)

    val hours = duration.toHours()
    val minutes = duration.toMinutes() % 60
    return "Remaining $hours h $minutes min"
}

Das dritte Beispiel — Arbeit mit der Retrofit-API. Der Server gibt einen ISO-8601-String mit Zone zurück. Wir verwenden einen benutzerdefinierten Deserialisierer zur Konvertierung in ZonedDateTime.

kotlin
data class EventResponse(
    @JsonAdapter(ZonedDateTimeAdapter::class)
    val eventTime: ZonedDateTime
)

class ZonedDateTimeAdapter : JsonAdapter<ZonedDateTime>() {
    override fun fromJson(reader: JsonReader): ZonedDateTime? {
        return ZonedDateTime.parse(
            reader.nextString()
        )
    }
}

Häufige Fehler bei der Arbeit mit Zeitzonen

Der erste Fehler — Verwendung von ZoneId.systemDefault() im Servercode. Die Serverzeitzone kann von der des Clients abweichen, und die Verwendung der Systemzone auf dem Server führt zu falschen Berechnungen. Geben Sie die Zone immer explizit an oder verwenden Sie UTC als Referenz.

Der zweite Fehler — Ignorieren der Sommerzeit bei der Dauerberechnung. Duration.between() behandelt Übergänge korrekt, aber wenn Sie Zeitstempel manuell subtrahieren, kann die Sommerzeit einen Fehler von 1 Stunde verursachen. Verwenden Sie ChronoUnit.HOURS.between() anstelle manueller Berechnungen.

Der dritte Fehler — Verwechslung von withZoneSameInstant und withZoneSameLocal. Ersteres ändert die Zone unter Beibehaltung des Moments — die Zeit verschiebt sich. Letzteres ändert die Zone unter Beibehaltung der lokalen Zeit — der Moment ändert sich. Die Wahl der falschen Methode ist laut SonarSource (2024) einer der häufigsten Fehler.

Der vierte Fehler — Annahme, dass die Gerätezeitzone immer der Benutzerzeitzone entspricht. Der Benutzer könnte reisen und erwarten, dass die App die Zeit in seiner „Heimat“-Zeitzone anzeigt, nicht in der aktuellen. Bieten Sie in diesem Fall die Zonenauswahl über die Benutzeroberfläche an.

Häufig gestellte Fragen

Was ist der Unterschied zwischen ZonedDateTime und OffsetDateTime?

ZonedDateTime enthält eine regionale Zonenkennung (z.B. „Europe/Moscow“) und behandelt DST. OffsetDateTime speichert nur einen festen Offset (+03:00) ohne regionale Regeln. Für die Datenbankspeicherung wird OffsetDateTime empfohlen.

Wie erhalte ich die aktuelle UTC-Zeit über ZonedDateTime?

Verwenden Sie ZonedDateTime.now(ZoneOffset.UTC) oder Instant.now().atZone(ZoneOffset.UTC). Beide Optionen geben den aktuellen Moment mit Null-Offset zurück. Für einen einfachen Zeitstempel verwenden Sie Instant.now() ohne Zonenbindung.

Kann ZonedDateTime über Gson oder Moshi serialisiert werden?

Ja, aber ein benutzerdefinierter Adapter ist erforderlich. Gson unterstützt ZonedDateTime standardmäßig nicht. Moshi unterstützt es über den Rfc3339DateJsonAdapter. Empfohlen wird die Verwendung von Kotlinx Serialization oder der JavaTimeModule-Bibliothek für Jackson.

Wie behandelt man die Situation, wenn die Zeit in eine DST-Lücke fällt?

java.time verschiebt die Zeit automatisch um den Offset-Betrag nach vorne. Wenn z.B. 02:30 nicht existiert, weil die Uhren auf 03:00 vorgestellt werden, erstellt ZonedDateTime ein Objekt um 03:30. Sie können über ZoneRules.getTransition(instant) auf eine Lücke prüfen.

Warum wird ZonedDateTime nicht für SQL-Datenbanken empfohlen?

JDBC 4.2 unterstützt OffsetDateTime, aber nicht direkt ZonedDateTime. ZonedDateTime enthält eine regionale Zone, die kein SQL-Äquivalent hat. Es wird empfohlen, OffsetDateTime oder Instant zu speichern und die Zone in einer separaten Spalte zu speichern.

Zusammenfassung

  • ZonedDateTime ist eine unveränderliche Klasse für Datum und Uhrzeit mit Zeitzone, die DST korrekt über die IANA Time Zone Database behandelt.
  • Der Hauptunterschied zu LocalDateTime ist das Vorhandensein einer Zone, was ZonedDateTime zu einem eindeutigen Identifikator eines Zeitpunkts macht.
  • Für die Konvertierung zwischen Zonen verwenden Sie withZoneSameInstant(), das den Moment bewahrt, nicht withZoneSameLocal.
  • Bei Sommerzeitumstellungen löst java.time Lücken und Überlappungen automatisch über integrierte Zonenregeln auf.
  • Für die Datenbankspeicherung verwenden Sie OffsetDateTime oder speichern Sie Instant und ZoneId getrennt.
  • Unter Android verwenden Sie zur Konvertierung von ZonedDateTime in die Geräte-Lokalzeit ZoneId.systemDefault() zusammen mit withZoneSameInstant.
  • Für die JSON-Serialisierung ist ein benutzerdefinierter Adapter erforderlich — verwenden Sie Kotlinx Serialization oder Jackson JavaTimeModule.

Wir entwickeln eine mobile Applikation schlüsselfertig

IT Sectr entwickelt seit 2017 iOS- und Android-Apps für Startups und Unternehmen. Wir beraten Sie und schlagen die beste Lösung vor.

Projekt besprechen

Lesen Sie auch