1. Begriff der Zeitzone
Auf der Welt gibt es viele Zeitzonen: Wenn es in Minsk Mittag ist, ist es in New York erst Morgen, und in Tokio bereits Abend. Wenn Sie Datum und Uhrzeit ohne Berücksichtigung der Zeitzone speichern, entsteht leicht Verwirrung: Wenn Ihr Server zum Beispiel in Deutschland steht, der Benutzer aber in Wladiwostok ist, bedeutet die Anzeige "2025-06-01 12:00" für jeden etwas völlig anderes.
Eine Zeitzone (timezone) ist eine Regel, die festlegt, wie viel Zeit zur Greenwich-Zeit (UTC) addiert oder von ihr subtrahiert werden muss, um die „lokale“ Zeit für eine bestimmte Region zu erhalten.
In Java wird für die Arbeit mit Zeitzonen die Klasse ZoneId verwendet. Hier einige Beispiele für Zonenkennungen:
- "Europe/Minsk"
- "UTC"
- "America/New_York"
- "Asia/Tokyo"
Warum ist das wichtig?
- Korrekte Anzeige der Zeit für Benutzer aus verschiedenen Ländern.
- Richtige Erfassung der Zeit von Ereignissen (z. B. Protokollierung, Ticketbuchung, Fristen).
- Berücksichtigung der Umstellung auf Sommer-/Winterzeit (Danke, Europa!).
2. ZonedDateTime – Datum und Uhrzeit mit Zeitzone
ZonedDateTime ist eine Klasse, die Datum, Uhrzeit und Informationen zur Zeitzone speichert. Es ist wie LocalDateTime, weiß aber zusätzlich, in welcher Region es sich befindet.
Erzeugung von ZonedDateTime
Aktuelles Datum und Uhrzeit in der Systemzeitzone
import java.time.ZonedDateTime;
ZonedDateTime now = ZonedDateTime.now();
System.out.println(now); // Zum Beispiel: 2025-06-01T15:30:00+03:00[Europe/Minsk]
Uhrzeit in einer bestimmten Zeitzone
import java.time.ZoneId;
ZonedDateTime MinskTime = ZonedDateTime.now(ZoneId.of("Europe/Minsk"));
ZonedDateTime newYorkTime = ZonedDateTime.now(ZoneId.of("America/New_York"));
System.out.println("Minsk: " + MinskTime);
System.out.println("New York: " + newYorkTime);
Erzeugung aus LocalDateTime
import java.time.LocalDateTime;
LocalDateTime meeting = LocalDateTime.of(2025, 6, 1, 18, 0);
ZonedDateTime meetingInMinsk = meeting.atZone(ZoneId.of("Europe/Minsk"));
System.out.println(meetingInMinsk); // 2025-06-01T18:00+03:00[Europe/Minsk]
Abrufen und Setzen der Zeitzone
ZoneId tokyoZone = ZoneId.of("Asia/Tokyo");
ZonedDateTime tokyoTime = ZonedDateTime.now(tokyoZone);
System.out.println("Tokio: " + tokyoTime);
Umrechnung zwischen Zonen: withZoneSameInstant()
Manchmal muss man wissen, wie dasselbe Ereignis in einer anderen Zone aussieht. Dafür verwenden wir withZoneSameInstant():
ZonedDateTime MinskMeeting = ZonedDateTime.of(2025, 6, 1, 18, 0, 0, 0, ZoneId.of("Europe/Minsk"));
ZonedDateTime newYorkMeeting = MinskMeeting.withZoneSameInstant(ZoneId.of("America/New_York"));
System.out.println("Uhrzeit des Treffens in Minsk: " + MinskMeeting);
System.out.println("Dasselbe Ereignis in New York: " + newYorkMeeting);
Achtung: withZoneSameInstant() rechnet die Zeit so um, dass sie demselben Zeitpunkt in einer anderen Zone entspricht. Wenn Sie withZoneSameLocal() verwenden, bleiben Datum und Uhrzeit gleich, aber die Zone ändert sich – das ist fast immer ein Fehler!
3. Instant – absoluter Zeitpunkt
Instant ist eine Klasse, die den absoluten Zeitpunkt unabhängig von der Zeitzone darstellt. Technisch ist das die Anzahl an Sekunden und Nanosekunden seit dem 1. Januar 1970 nach Greenwich (UTC). Hätte die Zeit einen Pass, wäre Instant die Ausweisnummer.
Erzeugung von Instant
import java.time.Instant;
Instant now = Instant.now();
System.out.println(now); // Zum Beispiel: 2025-06-01T12:30:00.123Z
Beachten Sie den Buchstaben Z – das bedeutet „Zulu time“, also UTC.
Erzeugung aus Sekunden seit der Unix-Epoche
Instant fromEpoch = Instant.ofEpochSecond(1685616000L);
System.out.println(fromEpoch); // 2023-06-01T00:00:00Z
Konvertierung Instant ↔ ZonedDateTime/LocalDateTime
Von ZonedDateTime zu Instant
ZonedDateTime zoned = ZonedDateTime.now();
Instant instant = zoned.toInstant();
System.out.println(instant);
Von Instant zu ZonedDateTime
ZoneId zone = ZoneId.of("Europe/Minsk");
ZonedDateTime fromInstant = Instant.now().atZone(zone);
System.out.println(fromInstant);
Von Instant zu LocalDateTime
import java.time.LocalDateTime;
import java.time.Instant;
import java.time.ZoneId;
LocalDateTime local = LocalDateTime.ofInstant(Instant.now(), ZoneId.of("Europe/Minsk"));
System.out.println(local);
4. Praxis: aktuelle Uhrzeit in verschiedenen Zeitzonen, Umrechnung zwischen Zonen
Aktuelle Uhrzeit in verschiedenen Zeitzonen ermitteln
Wir bauen eine Mini-Anwendung, die die aktuelle Uhrzeit in Minsk, New York und Tokio anzeigt:
import java.time.ZonedDateTime;
import java.time.ZoneId;
public class TimeZonesDemo {
public static void main(String[] args) {
ZonedDateTime Minsk = ZonedDateTime.now(ZoneId.of("Europe/Minsk"));
ZonedDateTime newYork = ZonedDateTime.now(ZoneId.of("America/New_York"));
ZonedDateTime tokyo = ZonedDateTime.now(ZoneId.of("Asia/Tokyo"));
System.out.println("Minsk: " + Minsk);
System.out.println("New York: " + newYork);
System.out.println("Tokio: " + tokyo);
}
}
Zeit zwischen Zonen umrechnen
Angenommen, Sie haben ein Ereignis, das auf 18:00 Uhr in Minsk festgelegt ist. Wie finden Sie heraus, wie spät es in New York und Tokio sein wird?
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.ZonedDateTime;
public class MeetingTime {
public static void main(String[] args) {
LocalDateTime eventTime = LocalDateTime.of(2025, 6, 1, 18, 0);
ZonedDateTime minskEvent = eventTime.atZone(ZoneId.of("Europe/Minsk"));
ZonedDateTime newYorkEvent = minskEvent.withZoneSameInstant(ZoneId.of("America/New_York"));
ZonedDateTime tokyoEvent = minskEvent.withZoneSameInstant(ZoneId.of("Asia/Tokyo"));
System.out.println("Treffen in Minsk: " + minskEvent);
System.out.println("In New York: " + newYorkEvent);
System.out.println("In Tokio: " + tokyoEvent);
}
}
Konvertierung von LocalDateTime zu ZonedDateTime und zurück
Local → Zoned:
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.ZonedDateTime;
LocalDateTime localTime = LocalDateTime.of(2025, 6, 1, 14, 0);
ZonedDateTime zonedTime = localTime.atZone(ZoneId.of("Europe/Minsk"));
System.out.println(zonedTime);
Zoned → Local:
LocalDateTime extracted = zonedTime.toLocalDateTime();
System.out.println(extracted);
5. Wichtige Hinweise und Besonderheiten
Warum man nicht nur LocalDateTime speichern sollte
LocalDateTime ist einfach nur Datum und Uhrzeit ohne Zeitzone. Für die meisten Geschäftslogiken ist das unzureichend! Wenn Sie zum Beispiel "2025-06-01 12:00" als LocalDateTime speichern, dann sind das für einen Benutzer in Minsk und einen in New York völlig unterschiedliche Zeitpunkte in der realen Zeit.
Speichern Sie immer die absolute Zeit (z. B. Instant) oder die Zeit mit Zone (ZonedDateTime), wenn ein Ereignis tatsächlich an eine bestimmte Zone gebunden ist. LocalDateTime ist nur dann sinnvoll, wenn Sie mit „floating“ Daten arbeiten (z. B. Geburtstag ohne Tageszeit und Zone).
Probleme bei der Umstellung auf Sommer-/Winterzeit
Zeitzonen sind nicht nur die Abweichung zu UTC, sondern auch Regeln für die Umstellung auf Sommer-/Winterzeit. In einigen Ländern wird an einem bestimmten Tag die Uhr um eine Stunde vor- oder zurückgestellt – und wenn Sie nur LocalDateTime speichern, erfahren Sie nicht, ob diese Uhrzeit überhaupt existiert hat.
Beispiel einer „Zeitlücke“:
- In den USA wird im März um 2:00 Uhr die Uhr auf 3:00 Uhr vorgestellt.
- Die Uhrzeit "2025-03-10 02:30" hat es in New York nicht gegeben!
Mit ZonedDateTime sind Sie gegen solche Überraschungen geschützt: Die Bibliothek prüft die Korrektheit der Zeit automatisch.
Schema: Wie hängen LocalDateTime, ZonedDateTime, Instant zusammen?
graph TD
A["LocalDateTime
(Datum + Uhrzeit, ohne Zone)"] -->|+ ZoneId| B["ZonedDateTime
(Datum + Uhrzeit + Zone)"]
B -->|"toInstant()"| C["Instant
(absolute Zeit, UTC)"]
C -->|"atZone(ZoneId)"| B
B -->|"toLocalDateTime()"| A
6. Typische Fehler bei der Arbeit mit ZonedDateTime und Instant
Fehler Nr. 1: LocalDateTime für globale Ereignisse verwenden.
Wenn Sie Datum und Uhrzeit eines Treffens von Benutzern aus verschiedenen Ländern als LocalDateTime speichern, sieht jeder sein „12:00“, obwohl es sich um unterschiedliche Zeitpunkte handelt. Für globale Ereignisse verwenden Sie ZonedDateTime oder Instant.
Fehler Nr. 2: Zeitzone beim Parsen eines Strings ignorieren.
Wenn Sie den String "2025-06-01T12:00:00" ohne Angabe der Zone parsen, erhalten Sie LocalDateTime und nicht ZonedDateTime. Um ZonedDateTime zu erhalten, verwenden Sie Strings mit Zone oder fügen Sie sie explizit hinzu.
Fehler Nr. 3: Falsche Umrechnung zwischen Zonen.
Die Verwendung von withZoneSameLocal() statt withZoneSameInstant() kann zu falschen Zeiten führen. Verwenden Sie immer withZoneSameInstant(), wenn Sie denselben Zeitpunkt in einer anderen Zone erhalten wollen.
Fehler Nr. 4: Umstellung auf Sommer-/Winterzeit nicht berücksichtigen.
Wenn Sie Ereignisse an der Grenze einer Umstellung planen, verwenden Sie unbedingt ZonedDateTime und vertrauen Sie der Bibliothek – sie kennt alle Umstellungen und „Lücken“ in der Zeit.
Fehler Nr. 5: ZonedDateTime ohne Berücksichtigung der Zone vergleichen.
Zwei ZonedDateTime mit unterschiedlichen Zonen, aber identischer Lokalzeit, können unterschiedliche Zeitpunkte darstellen. Verwenden Sie zum Vergleichen toInstant().
GO TO FULL VERSION