W3docs

Java LocalDateTime

Datum und Uhrzeit ohne Zeitzoneninformation in Java mit LocalDateTime kombinieren.

LocalDateTime ist LocalDate und LocalTime zusammengefügt — ein Kalenderdatum und eine Uhrzeit, aber immer noch ohne Zeitzone. Es ist der natürliche Wert für „dieses Ereignis fand um 14:30 Uhr am 4. November statt", und es ist der am häufigsten verwendete java.time-Typ in realen Codebasen, die keine globalen Nutzer berücksichtigen müssen.

Die fluente API hat dieselbe Form wie die beiden Hälften — dieselben now/of/parse-Factorymethoden, dieselben plusX/minusX/withX-Modifikatoren, dieselben isBefore/isAfter-Vergleiche. Die interessanten Teile sind die neuen: das Kombinieren mit einem Datum oder einer Uhrzeit, das Zerlegen zurück in die Bestandteile und die explizite Konvertierung zu ZonedDateTime, wenn die Zeitzone schließlich eine Rolle spielt.

Erstellen

LocalDateTime now    = LocalDateTime.now();                  // JVM default zone
LocalDateTime made   = LocalDateTime.of(2025, 11, 4, 14, 30);
LocalDateTime made2  = LocalDateTime.of(2025, Month.NOVEMBER, 4, 14, 30, 15);
LocalDateTime made3  = LocalDateTime.of(LocalDate.of(2025, 11, 4), LocalTime.of(14, 30));
LocalDateTime parsed = LocalDateTime.parse("2025-11-04T14:30:00");   // ISO-8601

Die ISO-8601-Form hat ein wörtliches T zwischen Datum und Uhrzeit — das ist das standardmäßige Trennzeichen. LocalDateTime.parse("2025-11-04 14:30:00") (Leerzeichen statt T) lässt sich mit dem Standard nicht parsen; dafür wäre ein benutzerdefinierter DateTimeFormatter erforderlich.

Zerlegung

LocalDate date = dt.toLocalDate();
LocalTime time = dt.toLocalTime();

Dies ist die Umkehrung von LocalDate.atTime(time) / LocalTime.atDate(date). Beides ist reine Projektion — kein Informationsverlust, keine eingeführte Zeitzone.

Alle Accessoren auf einen Blick

LocalDateTime erbt das Accessor-Menü beider Hälften:

dt.getYear();          dt.getMonth();        dt.getMonthValue();
dt.getDayOfMonth();    dt.getDayOfWeek();    dt.getDayOfYear();
dt.getHour();          dt.getMinute();       dt.getSecond();          dt.getNano();

Gleiche Namen, gleiche Semantik. Es ist nicht nötig, toLocalDate() oder toLocalTime() aufzurufen, um an die einzelnen Teile zu gelangen — sie sind alle direkt verfügbar.

Arithmetik über die Grenze hinaus

Der entscheidende Unterschied zu LocalTime: LocalDateTime.plusHours(3) bei 23:00 läuft nicht lautlos über. Es rollt in den nächsten Tag:

LocalDateTime late = LocalDateTime.of(2025, 11, 4, 23, 0);
late.plusHours(3);   // 2025-11-05T02:00 — date advanced as expected

Das ist der Hauptgrund für die Verwendung von LocalDateTime statt LocalTime bei jeder Berechnung, die möglicherweise Mitternacht überschreitet. Die Mathematik entspricht dem, was man von einer echten Uhr erwarten würde, die weiß, welcher Tag es ist.

dt.plusDays(7);          dt.plusHours(36);        dt.plusMinutes(150);
dt.minusYears(1);        dt.minusSeconds(45);

dt.withYear(2026);       dt.withHour(0);          dt.withMinute(0);

Die plusMonths-Clamp-Regel aus LocalDate gilt auch hier: LocalDateTime.of(2025, 1, 31, 12, 0).plusMonths(1) ergibt 2025-02-28T12:00, nicht 2025-03-03T12:00. Der Clamp betrifft nur die Datumskomponente; die Uhrzeit bleibt unverändert.

Die Zeitzone fehlt absichtlich

Ein LocalDateTime ist kein Moment auf der globalen Zeitachse. LocalDateTime.of(2025, 11, 4, 9, 0) könnte 9 Uhr morgens in New York, 9 Uhr morgens in Berlin oder 9 Uhr morgens in Tokio sein — drei sehr unterschiedliche Instant-Werte, und LocalDateTime verrät nicht, welcher es ist. Wenn zwei LocalDateTime-Werte gleich sind, bedeutet das, dass die Datums- und Uhrzeitstrings gleich sind; es bedeutet nicht, dass die zugrundeliegenden Momente gleich sind.

Das ist ein Feature, kein Bug. Für „der Vertrag wird um 14:00 Uhr Ortszeit unterzeichnet, wo auch immer der Unterzeichner sich befindet" ist LocalDateTime genau die richtige Form. Für „der Server hat die Anfrage um ... empfangen" ist es die falsche Form — verwende Instant. Für „das Meeting beginnt um 14:00 Uhr New Yorker Zeit" ist es ebenfalls falsch — verwende ZonedDateTime.

Um in einen zonalen Moment zu konvertieren, muss die Zone explizit hinzugefügt werden:

ZonedDateTime ny = ldt.atZone(ZoneId.of("America/New_York"));
Instant       inst = ldt.atZone(ZoneId.systemDefault()).toInstant();

atZone(...) ist der entscheidende Aufruf — es ist der Moment, an dem das Typsystem dich zwingt, zu entscheiden, welche Zeitzone du meinst. Sobald du dich entschieden hast, ist die Konvertierung zu Instant mechanisch. Die nächsten beiden Kapitel (ZonedDateTime, Instant) behandeln die zonalen und globalen Formen im Detail.

Vergleichen

dt.isBefore(other);
dt.isAfter(other);
dt.isEqual(other);
dt.compareTo(other);

Die Reihenfolge ist lexikographisch nach (date, time). Die gleiche Warnung wie zuvor: Zwei LocalDateTime-Werte werden anhand ihrer String-Darstellungen von Datum und Uhrzeit verglichen, nicht anhand der zugrundeliegenden Momente — denn ohne Zeitzone gibt es keine zugrundeliegenden Momente.

Abstände

ChronoUnit.X.between funktioniert direkt:

long minutes = ChronoUnit.MINUTES.between(start, end);
long days = ChronoUnit.DAYS.between(start, end);
Duration d = Duration.between(start, end);

Duration.between funktioniert mit LocalDateTime (es funktioniert mit jedem Temporal). Für rein kalendarische Arithmetik — „wie viele Monate liegen zwischen diesen zwei LocalDateTime-Werten" — verwende ChronoUnit.MONTHS.between, das einen long zurückgibt, oder Period.between(start.toLocalDate(), end.toLocalDate()) für die kalendarisch aufgeschlüsselte Form.

Ein durchgearbeitetes Beispiel: Planung über Mitternacht hinaus

Das folgende Programm verwendet LocalDateTime für ein kleines Stück Planungscode: eine Nachtschicht, die um 22:00 Uhr beginnt und um 06:00 Uhr endet, mit korrekter Dauerberechnung über Mitternacht hinaus; das Aufrunden von „jetzt" auf das nächste Viertelstundenintervall; das Finden des nächsten Vorkommens eines wiederkehrenden 09:30-Uhr-Meetings; und die Demonstration der Regel, dass die Zeitzone explizit hinzugefügt werden muss, wenn in einen Moment in der Zeit konvertiert wird.

java— editable, runs on the server

Was man aus der Ausführung mitnehmen kann:

  • Duration.between(startShift, endShift) ergab PT8H. Die Schicht überschritt Mitternacht, und die Datumskomponenten trugen den Übertrag weiter — es gab keine Mehrdeutigkeit. Dieselbe Berechnung mit bloßen LocalTime-Werten hätte PT-16H zurückgegeben (die LocalTime-Falle aus dem vorherigen Kapitel). Für Arithmetik, die möglicherweise Mitternacht überschreitet, ist LocalDateTime der richtige Typ.
  • Ausgehend von 20:00 blieb plusHours(3) bei 11-04 (23:00, kein Mitternachtsübergang); plusHours(5) rollte vorwärts zu 11-05T01:00. Die plus/minus-Familie auf LocalDateTime propagiert Überträge korrekt durch die gesamte Y/M/D/h/m/s/ns-Kette. Im eigenen Code ist kein Sonderfall erforderlich.
  • Der Block „nächstes 09:30-Meeting" baute das heutige 09:30 mit drei withX-Aufrufen und wählte dann anhand von isBefore zwischen heute und morgen. Das ist das typische Muster für „das nächste wiederkehrende Ereignis zu dieser Tageszeit" — klein genug zum Inlinen, häufig genug, um in einen Helfer auszulagern, wenn man viele davon hat.
  • Der Block „gleiche LocalDateTime, verschiedene Zeitzonen" erzeugte zwei verschiedene Instant-Werte mit sechs Stunden Abstand. Das ist der zentrale Grund, warum LocalDateTime nicht behauptet, ein Moment zu sein. Die Klasse weigert sich vorzutäuschen, dass ein Datum und eine Uhrzeit allein ausreichende Information sind; es ist ein Label auf einer Wanduhr irgendwo, und welche Wand, hängt von der angegebenen Zeitzone ab.
  • Die abschließende Unveränderlichkeitsprüfung zeigte now unverändert nach plusDays(7).withHour(0).withMinute(0). Diese Garantie gilt für jede Operation, jede Kette, jeden Helfer — es gibt keine Möglichkeit, ein LocalDateTime zu verändern. Gib es frei weiter, teile es zwischen Threads, speichere es in einer Map.

Was kommt als Nächstes

LocalDateTime ist der letzte der drei „Local"-Typen — keine Zeitzone, kein Anspruch darauf, ein Moment zu sein. Das nächste Kapitel, Java ZonedDateTime, fügt die Zeitzone explizit hinzu: ein LocalDateTime plus eine ZoneId plus den aufgelösten Offset für diese Ortszeit in dieser Zone, die zusammen einen tatsächlichen Moment auf der globalen Zeitachse bestimmen.

Übung

Übung
Zwei Entwickler — einer in New York, einer in Berlin — bauen beide den Wert `LocalDateTime.of(2025, 11, 4, 14, 0)`. Sind das derselbe Moment in der Zeit?
Zwei Entwickler — einer in New York, einer in Berlin — bauen beide den Wert `LocalDateTime.of(2025, 11, 4, 14, 0)`. Sind das derselbe Moment in der Zeit?
Was this page helpful?