Java LocalDate
Datum ohne Uhrzeit oder Zeitzone in Java mit LocalDate — Erstellung, Manipulation und Abfragen.
LocalDate ist ein Kalenderdatum — Jahr, Monat, Tag — ohne Tageszeit und ohne Zeitzone. Es repräsentiert dasselbe Datum auf jeder Uhr überall: Wenn Sie LocalDate.of(2025, 11, 4) schreiben, ist das der vierte November im ISO-Kalender, Punkt. Keine 14:30 angehängt, kein UTC-Offset, keine Tokyo-vs-Honolulu-Mehrdeutigkeit.
Das macht es zum richtigen Typ für viele Dinge, die das veraltete java.util.Date falsch behandelt hat: Geburtstage, Vertragsdaten, Rechnungsdaten, das Datum, das ein UI-Datumswähler ausgewählt hat. Überall wo ein Kalendertag die Einheit ist, ist LocalDate die richtige Klasse.
Erstellen
Die drei Standard-Fabriken:
LocalDate today = LocalDate.now(); // system default zone
LocalDate stardate = LocalDate.of(2025, 11, 4); // year, month (1-12), day (1-31)
LocalDate parsed = LocalDate.parse("2025-11-04"); // ISO-8601 yyyy-MM-ddnow() liest das aktuelle Datum in der Standardzeitzone der JVM. Das ist fast immer das, was Sie wollen; bei Tests ist es ein Problem, und die Clock-überladenen Formen (LocalDate.now(clock)) ermöglichen es Ihnen, eine feste Uhr einzusetzen. Das Parsing-Kapitel behandelt parse mit benutzerdefinierten Formaten; der Standard akzeptiert nur ISO-8601-Daten.
Sie können auch ein Month-Enum anstelle einer 1..12-Ganzzahl verwenden:
LocalDate.of(2025, Month.NOVEMBER, 4); // type-safe; no risk of using 0 for JanuaryWenn Sie jemals new GregorianCalendar(2025, 11, 4) geschrieben und Dezember erhalten haben (weil die veraltete API nullbasierte Monate verwendet), ist die Enum-Form die Verbesserung, die Sie möchten.
Abfragen
Der Accessor-Katalog:
int year = date.getYear();
Month month = date.getMonth(); // enum
int monthVal = date.getMonthValue(); // 1-12
int day = date.getDayOfMonth();
DayOfWeek dow = date.getDayOfWeek(); // enum: MONDAY, TUESDAY, ...
int dayOfYear = date.getDayOfYear(); // 1-366
boolean leap = date.isLeapYear();
int monthLen = date.lengthOfMonth(); // 28-31
int yearLen = date.lengthOfYear(); // 365 or 366Month und DayOfWeek sind Enums. Verwenden Sie sie; sie machen Code, der mit einem bestimmten Tag oder Monat vergleicht, erheblich klarer:
if (date.getDayOfWeek() == DayOfWeek.MONDAY) ... // type-safe
if (date.getMonth() == Month.NOVEMBER) ... // no off-by-one riskJedes Enum hat eigene Hilfsmethoden — Month.length(boolean leap), DayOfWeek.getValue() gibt 1-7 zurück, wobei Montag = 1, und DayOfWeek.plus(7) für "denselben Tag, n Tage von jetzt."
Verändern — jede Methode gibt eine neue Instanz zurück
Die Arithmetikmethoden:
date.plusDays(7); // a week later
date.plusWeeks(2);
date.plusMonths(1); // careful: month length varies
date.plusYears(1);
date.minusDays(30);
date.minusYears(5);Und die "ein Feld ersetzen"-Formen:
date.withYear(2026);
date.withMonth(1);
date.withDayOfMonth(1);
date.withDayOfYear(1); // first day of the yearJede dieser Methoden gibt ein neues LocalDate zurück. Das Original bleibt unverändert. date.plusDays(7) und das Vergessen, das Ergebnis aufzufangen, ist ein No-Op — und ein Fehler, den wir alle mindestens einmal geschrieben haben.
Der Vorbehalt "Monatslänge variiert" für plusMonths: Wenn das Hinzufügen eines Monats auf einem Tag landen würde, der im Zielmonat nicht existiert, klemmt java.time auf den letzten Tag. LocalDate.of(2025, 1, 31).plusMonths(1) ist 2025-02-28 (oder 02-29 in einem Schaltjahr), nicht 2025-03-03. Das Verhalten ist dokumentiert und konsistent, bedeutet aber, dass plusMonths(1) und minusMonths(1) nicht immer Umkehroperationen sind.
Vergleichen
date.isBefore(other);
date.isAfter(other);
date.isEqual(other); // same as equals here; useful on ZonedDateTime
date.compareTo(other); // -1 / 0 / +1LocalDate implementiert Comparable<LocalDate>, daher sortiert es sich in jeder Collection natürlich. Für "liegt dieses Datum in [start, end]?" ist die typische Form !date.isBefore(start) && !date.isAfter(end).
Abstand: until und ChronoUnit.between
Wie viele Tage zwischen zwei Daten?
long days = ChronoUnit.DAYS.between(start, end); // a long; signed
long weeks = ChronoUnit.WEEKS.between(start, end);
long months = ChronoUnit.MONTHS.between(start, end);
Period diff = start.until(end); // a Period (years/months/days)ChronoUnit.X.between ist der richtige Aufruf für "wie viele ganze X liegen zwischen diesen?". until gibt ein Period zurück, was die kalenderförmige Aufschlüsselung ist — nützlich für "Sie sind seit 2 Jahren, 3 Monaten und 14 Tagen Mitglied."
Beachten Sie die Vorzeichenkonvention: between(start, end) ist positiv, wenn end nach start liegt, sonst negativ.
Die "Was ist der Wochentag..."-Abkürzung
Das Paket der temporalen Anpasser gibt Ihnen die Prädikate, die Sie sonst von Hand berechnen würden:
import static java.time.temporal.TemporalAdjusters.*;
date.with(firstDayOfMonth());
date.with(lastDayOfMonth());
date.with(firstDayOfNextMonth());
date.with(next(DayOfWeek.MONDAY)); // next Monday strictly after `date`
date.with(nextOrSame(DayOfWeek.MONDAY)); // today if today is Monday, else next
date.with(previousOrSame(DayOfWeek.SUNDAY));
date.with(lastInMonth(DayOfWeek.FRIDAY)); // last Friday of the monthDas Kapitel Temporal Adjusters behandelt diese eingehend. Für jetzt ist die Kernaussage: Schreiben Sie "nächster Montag nach diesem Datum" nicht von Hand; die Anpasser haben es bereits.
Zeitzonen-Vorbehalt
LocalDate hat keine Zone, daher muss LocalDate.now() eine auswählen, um zu wissen, welcher Kalendertag "jetzt" ist. Der Standard ist die Standardzone der JVM (ZoneId.systemDefault()). Wenn Sie auf einem Server laufen, der auf UTC eingestellt ist, und es in New York 23:30 Ortszeit ist, gibt LocalDate.now() das morgige Datum aus New Yorker Perspektive zurück — weil die Zone der JVM sagt, dass es bereits nach Mitternacht UTC ist.
Für ein Datum, das lokal zu einer bekannten Zone ist, übergeben Sie die Zone explizit:
LocalDate tokyoToday = LocalDate.now(ZoneId.of("Asia/Tokyo"));Das trifft in der Produktion genau dann, wenn der Laptop des Entwicklers in einer anderen Zone als der eingesetzte Server ist. Seien Sie explizit, wenn die Zone wichtig ist.
Ein ausgearbeitetes Beispiel: Rechnungsdatum-Arithmetik
Das folgende Programm verwendet LocalDate für die Art von Arbeit, die ein kleines Rechnungssystem erledigen würde — ein Rechnungsdatum generieren, Fälligkeitsdaten berechnen, überfällige Tage zählen, das Monatsende ermitteln und den nächsten Geschäftstag finden. Das ist die realistische Form von LocalDate-Code.
Was man aus der Ausführung mitnehmen sollte:
LocalDate.of(2025, Month.NOVEMBER, 4)war die sichere Form. Der ganzzahlige Überladung (2025, 11, 4) funktioniert, aber dasMonth-Enum macht es unmöglich, 0 für Januar zu verwenden — der veraltete Fehler vonGregorianCalendar. Wenn das zweite Argument beides sein kann, verwenden Sie das Enum.plusDays(30)gab ein neuesLocalDatezurück; das Drucken des Originals am Ende des Programms zeigte es unverändert. Jede Arithmetik- undwith*-Methode folgt dieser Regel, was den Typ durch Konstruktion thread-sicher macht. Kein defensives Kopieren nötig; das Übergeben einesLocalDatean eine Methode ist immer sicher.- Die
plusMonths(1)-Demo zeigte das Klammerverhalten: 31. Januar + 1 Monat = 28. Februar (oder 29. in einem Schaltjahr). Das Verhalten ist dokumentiert und konsistent, aberjan31.plusMonths(1).minusMonths(1)gibtJanuary 28zurück, nichtJanuary 31. Hin-und-Rückweg-und-Original-erwarten funktioniert fürplusDays/minusDays, nicht fürplusMonths/minusMonths. - Die temporalen Anpasser (
lastDayOfMonth,firstDayOfNextMonth,nextOrSame(MONDAY)) ersetzten mehrzeilige manuelle Kalenderberechnungen. Verkettet drücken sie "der erste Montag am oder nach dem ersten des nächsten Monats" in zwei Anpassern aus. Das nächste Kapitel über LocalTime und das dedizierte Kapitel Temporal Adjusters gehen tiefer. ChronoUnit.DAYS.between(invoice, today)gab ein vorzeichenbehafteteslongzurück. Das begleitendeinvoiceDate.until(today)gab einPeriodzurück — kalenderförmig, mit separaten Jahr/Monat/Tag-Feldern. Die beiden beantworten unterschiedliche Fragen:ChronoUnit.Xfür "wie viele ganze X,"Periodfür "in kalenderfreundlicher Form." Wählen Sie die, deren Form der gewünschten Ausgabe entspricht.
Was kommt als nächstes
LocalDate war die Datumsseite. Das nächste Kapitel, Java LocalTime, ist sein Spiegel — Tageszeit, ohne angehängtes Datum und ohne Zone. Dieselbe fließende API, kleinere Klasse, dieselben Unveränderlichkeitsgarantien.