1. Historischer Kontext: wie man vor Java 8 mit Datum und Zeit arbeitete
In längst vergangenen Zeiten (vor Java 8) verwendete man zur Arbeit mit Datum und Zeit die Klassen java.util.Date, java.util.Calendar und zum Formatieren — java.text.SimpleDateFormat. Das war einer dieser Fälle, in denen Entwickler weltweit seufzten und mit zusammengebissenen Zähnen etwas wie Folgendes schrieben:
import java.util.Date;
Date now = new Date();
System.out.println(now); // Gibt etwas Merkwürdiges aus wie "Wed Jun 05 14:15:22 MSK 2025"
Klingt einfach, aber in der Praxis war es längst nicht so rosig. Hier nur einige der „Freuden“ des alten APIs:
- Date ist ein veränderliches (mutable) Objekt. Man konnte es versehentlich ändern, was oft zu Bugs führte.
- Monate in Date und Calendar begannen bei null (Januar ist 0, Dezember — 11), Tage dagegen bei eins.
- SimpleDateFormat war nicht thread-sicher: Wenn zwei Threads gleichzeitig formatierten, konnte es zu unerwarteten Ergebnissen kommen.
- Eine große Anzahl von Methoden war als @Deprecated (veraltet) markiert, und die IDE erschreckte einen ständig mit gelben Warnungen.
- Die Arbeit mit Zeitzonen war ein echter Schmerz: Es war leicht, Lokalzeit und UTC zu verwechseln; an Sommer-/Winterzeitwechsel wollte man gar nicht denken.
Beispiel für den Schmerz
import java.util.Date;
Date date = new Date(2025, 5, 1); // Jahr 2025, 5. Monat (Juni?), 1. Tag
System.out.println(date); // Nicht das, was man erwartet!
2. Einführung von java.time: ein neuer Ansatz
Bis 2014 war klar: Das alte API ist nicht nur unbequem — es ist gefährlich. Deshalb erschien in Java das neue Paket java.time, das die Spezifikation JSR‑310 umsetzt. Dieses API war von der beliebten Bibliothek Joda-Time inspiriert und wurde sofort zum de-facto Standard.
Wichtige Pakete und Klassen
- java.time — das Hauptpaket, in dem alle neuen Klassen für Datum und Zeit leben.
- java.time.format — für Formatierung und Parsing von Datum und Zeit.
- java.time.temporal — für fortgeschrittene zeitliche Operationen.
- java.time.zone — für die Arbeit mit Zeitzonen.
Das sind die Hauptakteure des neuen APIs:
| Klasse | Wofür? | Beispiel |
|---|---|---|
|
Nur Datum (Jahr, Monat, Tag) | Geburtstag, ohne Uhrzeit |
|
Nur Zeit (Stunden, Minuten, Sekunden) | Besprechungszeit, ohne Datum |
|
Datum und Uhrzeit ohne Zeitzone | Lokales Ereignis |
|
Datum und Uhrzeit mit Zeitzone | Besprechung in Minsk zur Ortszeit |
|
Absoluter Zeitpunkt (UTC) | Ereignismarkierung im Log |
|
Zeitspanne (Stunden, Minuten, Sek.) | Gesprächsdauer |
|
Zeitraum (Jahre, Monate, Tage) | Berufserfahrung, Alter |
Beispiel: Datum auf die neue Art erstellen
import java.time.LocalDate;
LocalDate today = LocalDate.now();
System.out.println(today); // Zum Beispiel: "2025-06-05"
3. Vorteile des neuen APIs
Unveränderlichkeit (immutable)
Alle Klassen aus java.time sind unveränderlich. Das heißt: Wenn Sie ein LocalDate-Objekt erzeugt haben, lässt es sich nicht ändern. Jede Operation (z. B. einen Tag hinzufügen) liefert ein neues Objekt zurück.
LocalDate today = LocalDate.now();
LocalDate tomorrow = today.plusDays(1);
System.out.println(today); // 2025-06-05
System.out.println(tomorrow); // 2025-06-06
Expliziter Umgang mit Zeitzonen
Im alten API konnte man leicht vergessen, in welcher Zeitzone sich ein Datum befindet. In java.time ist alles explizit: Wenn Sie eine Zeitzone benötigen — verwenden Sie ZonedDateTime, wenn nicht — verwenden Sie LocalDateTime.
import java.time.ZonedDateTime;
import java.time.ZoneId;
ZonedDateTime MinskTime = ZonedDateTime.now(ZoneId.of("Europe/Minsk"));
System.out.println(MinskTime); // 2025-06-05T14:23:45+03:00[Europe/Minsk]
Bequeme Methoden für Berechnungen und Vergleiche
LocalDate today = LocalDate.now();
LocalDate nextMonth = today.plusMonths(1);
boolean isAfter = LocalDate.now().plusDays(1).isAfter(today); // true
Formatierung und Parsing
Formatierung und Parsing erfolgen über DateTimeFormatter (Details — in der nächsten Vorlesung):
import java.time.format.DateTimeFormatter;
LocalDate today = LocalDate.now();
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("dd.MM.yyyy");
String formatted = today.format(formatter); // "05.08.2025"
4. Kompatibilität: Umgang mit Legacy-Code
In der realen Welt muss man sehr oft mit Bibliotheken oder alten Systemen arbeiten, die Date und Calendar verwenden. Zum Glück ist das neue API legacy-freundlich: Man kann alte Typen in neue und umgekehrt konvertieren.
Umwandlung Date ↔ Instant
import java.util.Date;
import java.time.Instant;
// Date → Instant
Date legacyDate = new Date();
Instant instant = legacyDate.toInstant();
// Instant → Date
Date dateBack = Date.from(instant);
Umwandlung Calendar ↔ ZonedDateTime
import java.util.Calendar;
import java.time.ZonedDateTime;
import java.time.ZoneId;
import java.util.Date;
// Calendar → ZonedDateTime
Calendar calendar = Calendar.getInstance();
ZonedDateTime zdt = ZonedDateTime.ofInstant(
calendar.toInstant(),
calendar.getTimeZone().toZoneId()
);
// ZonedDateTime → Calendar
Calendar calBack = Calendar.getInstance();
calBack.setTime(Date.from(zdt.toInstant()));
Tabelle: Zuordnung alter und neuer Klassen
| Alter Typ | Neuer Typ | Kommentar |
|---|---|---|
|
|
Absolute Zeit |
|
|
Datum und Uhrzeit mit Zeitzone |
|
|
Formatierung/Parsing von Datumsangaben |
5. Praxis: erste Schritte mit java.time
Angenommen, Sie haben einen Benutzer mit Geburtsdatum. Speichern und geben wir dieses Datum aus:
import java.time.LocalDate;
public class UserProfile {
private String name;
private LocalDate birthDate;
public UserProfile(String name, LocalDate birthDate) {
this.name = name;
this.birthDate = birthDate;
}
public void printProfile() {
System.out.println("Name: " + name);
System.out.println("Geburtsdatum: " + birthDate);
}
}
public class Main {
public static void main(String[] args) {
UserProfile user = new UserProfile("Alice", LocalDate.of(1998, 12, 25));
user.printProfile();
}
}
Ausgabe:
Name: Alice
Geburtsdatum: 1998-12-25
6. Vergleich: altes API vs. neues API
Beispiel: eine Woche zum Geburtsdatum hinzufügen
Alter Ansatz (Date/Calendar):
import java.util.Calendar;
Calendar cal = Calendar.getInstance();
cal.set(1998, Calendar.DECEMBER, 25);
cal.add(Calendar.WEEK_OF_YEAR, 1);
System.out.println(cal.getTime()); // Mühsam und nicht offensichtlich
Neuer Ansatz (java.time):
import java.time.LocalDate;
LocalDate birthDate = LocalDate.of(1998, 12, 25);
LocalDate nextWeek = birthDate.plusWeeks(1);
System.out.println(nextWeek); // 1999-01-01
Im neuen API ist der Code kürzer, einfacher und sicherer.
7. Typische Fehler im Umgang mit java.time
Fehler Nr. 1: Man vergisst, dass Objekte unveränderlich sind.
Wenn Sie date.plusDays(1); aufrufen und das Ergebnis nicht speichern, bleibt das ursprüngliche Datum unverändert.
Fehler Nr. 2: Verwechslung von LocalDate und LocalDateTime.
LocalDate speichert nur das Datum (Jahr, Monat, Tag), LocalDateTime hingegen auch die Zeit. Verwechseln Sie das nicht, wenn Sie Stunden und Minuten verarbeiten müssen.
Fehler Nr. 3: Verwendung alter Klassen in neuen Projekten.
Wenn möglich – verwenden Sie stets java.time. Die alten Klassen werden nur für die Kompatibilität benötigt.
Fehler Nr. 4: Falscher Umgang mit Zeitzonen.
Wenn ein Ereignis für verschiedene Regionen relevant ist, verwenden Sie ZonedDateTime oder zumindest Instant. LocalDateTime enthält keine Information über die Zeitzone!
Fehler Nr. 5: Der Versuch, LocalDate und LocalDateTime direkt zu vergleichen.
Das sind unterschiedliche Datentypen; vergleichen kann man sie nicht direkt. Bringen Sie sie zunächst auf denselben Typ.
GO TO FULL VERSION