LocalDate, LocalTime und LocalDateTime: was sie sind, Arbeit mit Datum

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

LocalDate, LocalTime und LocalDateTime sind die Hauptklassen des Pakets java.time, die die Arbeit mit Datum und Zeit ohne Bindung an eine Zeitzone ermöglichen. Laut Oracle-Dokumentation (Java 17, 2024) sind diese Typen als immutable und thread-safe konzipiert, was sie für Multithread-Anwendungen sicher macht. Sie wurden auf Android ab API 26 durch Desugaring und für ältere Versionen — durch die Bibliothek ThreeTenABP — verfügbar gemacht.

Wichtige Punkte

  • LocalDate — eine immutable Klasse zur Darstellung eines Datums (Jahr, Monat, Tag) ohne Zeit und Zeitzone.
  • LocalTime — eine immutable Klasse zur Darstellung der Uhrzeit (Stunde, Minute, Sekunde, Nanosekunde) ohne Datum und Zeitzone.
  • LocalDateTime — eine Kombination von LocalDate und LocalTime, die sowohl Datum als auch Uhrzeit ohne Zeitzonenbindung speichert.
  • Alle drei Klassen unterstützen arithmetische Operationen — Addition und Subtraktion von Tagen, Monaten, Stunden über die Methoden plus und minus.
  • Auf Android sind diese Typen durch Desugaring (API 26+) oder die ThreeTenABP-Bibliothek (API < 26) verfügbar.

Was sind LocalDate, LocalTime und LocalDateTime?

LocalDate — eine Klasse, die ein Datum im Format Jahr-Monat-Tag ohne Zeit- und Zeitzoneninformationen darstellt. Sie wird verwendet, um Daten wie Geburtstage, Veranstaltungstermine oder Ablaufdaten zu speichern.

LocalDate speichert ein Jahr im Bereich von -999999999 bis +999999999, einen Monat von 1 bis 12 und einen Tag des Monats unter Berücksichtigung von Schaltjahren. Die Klasse ist vollständig immutable — jede Operation gibt ein neues Objekt zurück.

LocalTime repräsentiert die Tageszeit: Stunden, Minuten, Sekunden und Nanosekunden. Die maximale Genauigkeit beträgt eine Nanosekunde. LocalTime enthält keine Datums- oder Zeitzoneninformationen, was es praktisch zum Speichern von Öffnungszeiten oder Prozessdauern macht.

LocalDateTime kombiniert LocalDate und LocalTime in einem Objekt. Dies ist der am häufigsten verwendete Typ, wenn sowohl Datum als auch Uhrzeit gespeichert werden müssen, aber keine Zeitzonenbindung erforderlich ist. Zum Beispiel das Datum und die Uhrzeit eines Konzerts im lokalen Format.

Laut Oracle Java-Dokumentation (2024) sind alle drei Klassen auf der Grundlage von Ideen aus der Joda-Time-Bibliothek konzipiert, jedoch mit verbesserter Architektur und vollständiger Integration in die Standardbibliothek.

Wie funktioniert das Paket java.time?

Das Paket java.time wurde in Java 8 als Ersatz für die veralteten Klassen Date, Calendar und SimpleDateFormat eingeführt. Seine Architektur basiert auf den Prinzipien von immutable Objekten und einer fließenden Schnittstelle.

Eine wichtige Eigenschaft — alle Hauptklassen sind value-based. Das bedeutet, dass ihre Instanzen nach Wert und nicht nach Referenz verglichen werden und sie nicht vererbt werden können. Zum Vergleichen zweier Objekte wird die equals-Methode verwendet, nicht der ==-Operator.

Das Paket ist in mehrere Kategorien unterteilt. Typen ohne Zeitzone — LocalDate, LocalTime, LocalDateTime — werden für lokale Daten und Zeiten verwendet. Typen mit Zeitzone — ZonedDateTime, OffsetDateTime, OffsetTime — fügen Versatz- oder Zoneninformationen hinzu. Momentane Typen — Instant — stellen einen Punkt auf der Zeitachse in UTC dar.

Diese Unterteilung löst ein Problem, das der alten API innewohnte: Der Entwickler wusste nie, ob ein Date-Objekt Zeitzoneninformationen enthielt oder nicht. In java.time deklariert jeder Typ explizit seine Semantik.

LocalDate: Arbeit mit Datum

Die Klasse LocalDate bietet viele Methoden zum Erstellen, Lesen und Ändern von Daten. Das aktuelle Datum kann über die statische Methode now() abgerufen werden. Ein bestimmtes Datum — über die Methode of(int year, int month, int dayOfMonth).

Zum Lesen der Datumskomponenten werden Getter verwendet: getYear(), getMonthValue(), getDayOfMonth(), getDayOfWeek(), getDayOfYear(). Die Methode getMonth() gibt die Enumeration Month zurück, und getDayOfWeek() gibt die Enumeration DayOfWeek zurück.

LocalDate unterstützt die Überprüfung von Daten. Die Methoden isBefore(), isAfter() und isEqual() ermöglichen den Vergleich von Daten. Die Methode isLeapYear() prüft, ob das Jahr ein Schaltjahr ist. Die Methode lengthOfMonth() gibt die Anzahl der Tage im Monat zurück, lengthOfYear() die Anzahl der Tage im Jahr.

Zum Ändern werden die Methoden withYear(), withMonth(), withDayOfMonth() verwendet, die ein neues Objekt mit der geänderten Komponente zurückgeben. Die Methoden plusDays(), minusMonths() und ähnliche führen Datumsarithmetik durch.

LocalTime: Arbeit mit Zeit

LocalTime stellt die Tageszeit mit Nanosekundengenauigkeit dar. Das Standardformat ist ISO-8601 (HH:mm:ss.nnnnnnnnn). Der Mindestwert ist 00:00, der Höchstwert ist 23:59:59.999999999.

Ein LocalTime-Objekt kann mit now() für die aktuelle Zeit, of(int hour, int minute), of(int hour, int minute, int second) oder of(int hour, int minute, int second, int nanoOfSecond) erstellt werden. Die Methode parse(CharSequence text) analysiert einen String im ISO-8601-Format.

Zu den Gettern gehören getHour(), getMinute(), getSecond(), getNano(). Die Methode toSecondOfDay() gibt die Anzahl der Sekunden seit Tagesbeginn zurück, und toNanoOfDay() gibt Nanosekunden zurück. Dies ist praktisch für die Berechnung der Dauer innerhalb eines Tages.

LocalTime unterstützt dieselben Vergleichs- und Änderungsoperationen wie LocalDate: plusHours(), minusMinutes(), withHour(), withMinute(). Die Methoden isBefore() und isAfter() funktionieren unter Berücksichtigung, dass die Zeit innerhalb eines Tages zyklisch ist.

LocalDateTime: Kombination von Datum und Zeit

LocalDateTime vereint die Fähigkeiten von LocalDate und LocalTime in einer Klasse. Es speichert sowohl Datum als auch Uhrzeit, jedoch ohne Zeitzone. Dies ist der flexibelste lokale Typ, erfordert jedoch Vorsicht bei der Verwendung in verteilten Systemen.

LocalDateTime kann über die statischen Methoden now(), of(LocalDate date, LocalTime time), of(int year, Month month, int dayOfMonth, int hour, int minute) und deren Überladungen erstellt werden. LocalDate und LocalTime können auch über die Methode atTime() kombiniert werden.

LocalDateTime bietet Zugriff auf alle Datums- und Zeitfelder über entsprechende Getter: toLocalDate() und toLocalTime() geben einzelne Komponenten zurück. Die Methode truncatedTo(TemporalUnit unit) ermöglicht das Runden der Zeit auf eine bestimmte Genauigkeit — zum Beispiel auf Minuten.

Für die Konvertierung in eine Zeitzone wird die Methode atZone(ZoneId zone) verwendet, die ZonedDateTime zurückgibt. Dies ist die einzige Möglichkeit, LocalDateTime eine Zeitzone hinzuzufügen.

Wie erstellt man Datums- und Zeitobjekte?

Alle drei Klassen verwenden ein einheitliches Erstellungsmuster über statische Factory-Methoden. Die Klassenkonstruktoren sind als private deklariert — ein Objekt kann nicht direkt mit new erstellt werden.

Wichtigste Erstellungsmethoden:

  • now() — aktuelles Datum/Uhrzeit von der Systemuhr
  • of(...) — aus Komponenten (Jahr, Monat, Tag usw.)
  • parse(String) — aus einem String im ISO-8601-Format
  • from(TemporalAccessor) — aus einem anderen Temporal-Objekt

Die Methode of hat viele Überladungen. Für LocalDate werden Jahr, Monat und Tag benötigt. Für LocalTime — Stunden und Minuten (optional Sekunden und Nanosekunden). Für LocalDateTime — Jahr, Monat, Tag, Stunden, Minuten. Der Monat kann als int (1-12) oder als Enumeration Month übergeben werden.

kotlin
val today = LocalDate.now()
val specificDate = LocalDate.of(2026, Month.JULY, 21)
val parsedDate = LocalDate.parse("2026-07-21")

val currentTime = LocalTime.now()
val lunchTime = LocalTime.of(13, 30, 0)
val parsedTime = LocalTime.parse("13:30:00")

val now = LocalDateTime.now()
val meeting = LocalDateTime.of(2026, 7, 21, 15, 0)

Konvertierung zwischen Typen

Die java.time-Klassen sind für eine bequeme Konvertierung untereinander ausgelegt. LocalDate kann über die Methode atTime(LocalTime) oder atStartOfDay() in LocalDateTime konvertiert werden. LocalTime — über atDate(LocalDate).

LocalDateTime kann über toLocalDate() zurück in LocalDate und über toLocalTime() in LocalTime konvertiert werden. Für die Konvertierung in ZonedDateTime wird die Methode atZone(ZoneId) verwendet.

Die Konvertierung in java.util.Date (zur Kompatibilität mit Altsystemen) erfordert einen Zwischenschritt über Instant und eine Zeitzone. Laut Baeldung (2024) wird diese Operation über Date.from(instant) durchgeführt.

kotlin
val date = LocalDate.of(2026, 7, 21)
val dateTime = date.atTime(LocalTime.of(10, 30))

val time = LocalTime.of(14, 0)
val dateTimeFromTime = time.atDate(date)

val extractedDate = dateTime.toLocalDate()
val extractedTime = dateTime.toLocalTime()

val zoned = dateTime.atZone(ZoneId.of("Europe/Moscow"))

Formatierung und Parsing

Für Formatierung und Parsing wird die Klasse DateTimeFormatter verwendet. Sie bietet vordefinierte Formate über Konstanten (ISO_LOCAL_DATE, ISO_LOCAL_TIME, ISO_LOCAL_DATE_TIME) und die Möglichkeit, eigene Formate über Musterstrings zu erstellen.

Formatierungsmuster verwenden Symbole: yyyy — Jahr, MM — Monat (zweistellig), dd — Tag, HH — Stunde (0-23), mm — Minute, ss — Sekunde. Die Methode format() wird auf dem Datums-Zeit-Objekt oder über DateTimeFormatter aufgerufen.

DateTimeFormatter unterstützt auch die Lokalisierung über die statischen Methoden ofLocalizedDate(FormatStyle), ofLocalizedTime(FormatStyle) und ofLocalizedDateTime(FormatStyle). Verfügbare Stile sind SHORT, MEDIUM, LONG und FULL.

kotlin
val formatter = DateTimeFormatter.ofPattern("dd.MM.yyyy HH:mm")
val formatted = LocalDateTime.now().format(formatter)

val parsed = LocalDate.parse(
    "21.07.2026",
    DateTimeFormatter.ofPattern("dd.MM.yyyy")
)

Vergleich von Datums-Zeit-Objekten

Alle drei Klassen implementieren das Interface Comparable, was einen natürlichen Vergleich ermöglicht. Die Methode compareTo() gibt eine negative Zahl, Null oder eine positive Zahl je nach Reihenfolge zurück. Die Methoden isBefore(), isAfter() und isEqual() geben einen booleschen Wert zurück.

Bei LocalDate erfolgt der Vergleich chronologisch — ein früheres Datum gilt als kleiner. Bei LocalTime — nach Tageszeit. Bei LocalDateTime — zuerst nach Datum, dann nach Zeit. Alle Vergleiche berücksichtigen korrekt Schaltjahre und die Anzahl der Tage in Monaten.

Ein wichtiger Unterschied zur alten API: equals() für LocalDate, LocalTime und LocalDateTime vergleicht Werte, nicht Referenzen. Das bedeutet, dass zwei Objekte mit denselben Feldern gleich sind, auch wenn es sich um verschiedene Instanzen handelt.

kotlin
val d1 = LocalDate.of(2026, 7, 21)
val d2 = LocalDate.of(2026, 12, 25)

if (d1.isBefore(d2)) {
    Log.d("Datum", "d1 ist vor d2")
}

val sortedDates = listOf(d2, d1).sorted()

Datums- und Zeitarithmetik

Alle drei Klassen unterstützen arithmetische Operationen über die Methoden plus und minus. Für LocalDate stehen plusDays(), plusWeeks(), plusMonths(), plusYears() und die entsprechenden minus-Methoden zur Verfügung. LocalTime unterstützt plusHours(), plusMinutes(), plusSeconds(), plusNanos().

LocalDateTime erbt alle arithmetischen Operationen beider Typen. Eine bemerkenswerte Eigenschaft von LocalDate: Beim Hinzufügen eines Monats verarbeiten die Ergebnisse korrekt unterschiedliche Monatslängen. Zum Beispiel 31. Januar + 1 Monat = 28. (29. in einem Schaltjahr) Februar.

Für komplexere Operationen gibt es die Klasse Period (für Daten) und Duration (für Zeit). Die Methoden plus(TemporalAmount) und minus(TemporalAmount) akzeptieren diese Objekte.

kotlin
val today = LocalDate.now()
val nextWeek = today.plusDays(7)
val nextMonth = today.plusMonths(1)
val lastYear = today.minusYears(1)

val now = LocalTime.now()
val inTwoHours = now.plusHours(2)
val halfHourAgo = now.minusMinutes(30)

Kotlin-Codebeispiele

Betrachten wir ein praktisches Beispiel: eine Anwendung zur Erfassung von Arbeitsschichten. Wir müssen die Schichtdauer berechnen und feststellen, ob sie in die Nachtzeit fällt. Wir verwenden LocalTime für Start- und Endzeit, LocalDate für das Datum und LocalDateTime für die Berechnung von Schichten, die Mitternacht überschreiten.

kotlin
data class Shift(
    val startTime: LocalTime,
    val endTime: LocalTime,
    val date: LocalDate
) {
    fun isOvernight(): Boolean = endTime.isBefore(startTime)

    fun durationInMinutes(): Long {
        val start = LocalDateTime.of(date, startTime)
        val end = LocalDateTime.of(
            if (isOvernight()) date.plusDays(1) else date,
            endTime
        )
        return Duration.between(start, end).toMinutes()
    }
}

Ein zweites Beispiel — Berechnung des Alters eines Benutzers. Wir verwenden LocalDate für das Geburtsdatum und vergleichen es mit dem aktuellen Datum unter Berücksichtigung von Tag und Monat der Geburt.

kotlin
fun calculateAge(birthDate: LocalDate): Int {
    val today = LocalDate.now()
    val period = Period.between(birthDate, today)
    return period.years
}

Ein drittes Beispiel — Arbeit mit Benachrichtigungen. LocalDateTime wird zum Planen von Erinnerungen verwendet. Wir überprüfen, ob die geplante Zeit erreicht wurde.

kotlin
data class Reminder(
    val id: Long,
    val scheduledAt: LocalDateTime
) {
    fun isDue(): Boolean =
        LocalDateTime.now().isAfter(scheduledAt)
}

Android-Unterstützung: API-Level und Desugaring

Die integrierte Unterstützung für java.time erschien auf Android ab API 26 (Android 8.0 Oreo). Für Geräte mit älteren Android-Versionen muss Desugaring verwendet werden — ein Mechanismus, der Unterstützung für neue Java-APIs in früheren Versionen hinzufügt.

Desugaring im Android Gradle Plugin wird über compileOptions in build.gradle konfiguriert. Es genügt, isCoreLibraryDesugaringEnabled = true zu setzen und die Bibliothek desugar_jdk_libs hinzuzufügen. Danach ist java.time für alle API-Level ab 14 verfügbar.

Für Projekte, die Desugaring nicht verwenden können (z. B. Legacy-Projekte mit AGP unter 4.0), gibt es die Bibliothek ThreeTenABP — einen Backport von java.time. Sie stellt dieselben Klassen (LocalDate, LocalTime, LocalDateTime) bereit, jedoch im Paket org.threeten.bp.

groovy
@Suppress("UnstableApiUsage")
android {
    compileOptions {
        isCoreLibraryDesugaringEnabled = true
    }
}

dependencies {
    "coreLibraryDesugaring"("com.android.tools:desugar_jdk_libs:2.1.4")
}

Häufige Fehler und wie man sie vermeidet

Der erste häufige Fehler — die Verwendung von LocalDateTime in verteilten Systemen ohne Berücksichtigung von Zeitzonen. Wenn der Server in Europe/Moscow und der Client in Asia/Tokyo ist, wird LocalDateTime unterschiedlich interpretiert. Lösung: Verwenden Sie Instant oder ZonedDateTime für globale Daten.

Der zweite Fehler — falsches String-Parsing. Standardmäßig erwartet LocalDate.parse() das ISO-8601-Format (yyyy-MM-dd). Wenn der String ein anderes Format hat, müssen Sie explizit einen DateTimeFormatter übergeben. Sie sollten auch DateTimeParseException behandeln, damit die Anwendung bei ungültiger Eingabe nicht abstürzt.

Der dritte Fehler — Missachtung der Nullsicherheit. LocalDate, LocalTime und LocalDateTime sind Objekte, die null sein können. In Kotlin wird empfohlen, nullable Typen mit expliziten Prüfungen oder dem Elvis-Operator zu verwenden. In Java — vor dem Aufruf von Methoden auf null prüfen.

Der vierte Fehler — Verwechslung von LocalDateTime und ZonedDateTime. LocalDateTime enthält keine Zeitzoneninformationen. Wenn Sie einen absoluten Zeitpunkt übergeben müssen — verwenden Sie zonale Typen. Wenn die lokale Zeit ausreicht — verwenden Sie lokale Typen.

Häufig gestellte Fragen

Was ist der Unterschied zwischen LocalDate und Date in Java?

Date speichert die Anzahl der Millisekunden seit 1970-01-01 UTC, während LocalDate Jahr, Monat und Tag ohne Zeitzonenbindung speichert. Date ist veränderbar und nicht thread-safe, LocalDate ist immutable und thread-safe. Date ist seit Java 8 veraltet.

Kann LocalDateTime in einer Datenbank verwendet werden?

Ja, LocalDateTime wird gut auf den SQL-Typ TIMESTAMP WITHOUT TIME ZONE abgebildet. JPA und Room unterstützen es über TypeConverter. Für TIMESTAMP WITH TIME ZONE verwenden Sie ZonedDateTime oder OffsetDateTime.

Wie erhält man die Anzahl der Tage zwischen zwei Daten?

Verwenden Sie ChronoUnit.DAYS.between(startDate, endDate). Diese Methode gibt einen long-Wert zurück — die Differenz in Tagen. Für eine detailliertere Berechnung verwenden Sie Period.between(), das einen Period mit Jahren, Monaten und Tagen zurückgibt.

Was tun, wenn die Zeitgenauigkeit bis auf Millisekunden erhalten bleiben muss?

LocalTime unterstützt Nanosekundengenauigkeit (9 Dezimalstellen). Wenn Millisekundengenauigkeit ausreicht, verwenden Sie truncateTo(ChronoUnit.MILLIS) vor dem Speichern. Dies verhindert Rundungsprobleme bei der Serialisierung.

Warum gibt LocalDate.now() auf verschiedenen Geräten unterschiedliche Daten zurück?

Die Methode now() verwendet die Systemuhr und die Standardzeitzone des Geräts. Wenn Geräte sich in unterschiedlichen Zeitzonen befinden, kann das Datum abweichen. Für einen einheitlichen Zeitstempel verwenden Sie Instant.now(), das immer die UTC-Zeit zurückgibt.

Zusammenfassung

  • LocalDate — eine immutable Klasse für Datum ohne Zeit und Zeitzone. Wird zum Speichern von Geburtstagen, Fristen, Ereignisdaten verwendet.
  • LocalTime — eine immutable Klasse für Tageszeit mit Nanosekundengenauigkeit. Geeignet zum Speichern von Öffnungszeiten, Prozessdauern.
  • LocalDateTime — Kombination von Datum und Zeit ohne Zeitzonenbindung. Der flexibelste lokale Typ, aber nicht für verteilte Systeme geeignet.
  • Alle drei Klassen unterstützen Arithmetik, Vergleich, Formatierung und Parsing über eine einheitliche API basierend auf DateTimeFormatter.
  • Auf Android ist java.time ab API 26 integriert verfügbar oder über Desugaring für ältere Versionen.
  • Für globale Zeitstempel und zeitzonenbezogene Daten verwenden Sie ZonedDateTime oder Instant anstelle lokaler Typen.
  • Beim Parsen von Strings übergeben Sie immer einen DateTimeFormatter für nicht standardisierte Formate und behandeln DateTimeParseException.

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