Dates and times
Use java.time. The older classes are still there and should not be.
LocalDate today = LocalDate.now();
LocalDate d = LocalDate.of(2026, 8, 28);
LocalDate later = d.plusDays(30);
Which type
| Type | Holds | Use for |
|---|---|---|
LocalDate |
a date, no time, no zone | birthdays, invoice dates |
LocalTime |
a time, no date | opening hours |
LocalDateTime |
both, no zone | a wall clock reading |
Instant |
a point on the UTC timeline | timestamps, logs, durations |
ZonedDateTime |
an instant in a named zone | scheduling for a place |
Duration |
elapsed time | measuring |
Period |
calendar amount | “three months later” |
The important distinction is LocalDateTime versus Instant. LocalDateTime is what a clock on a wall says and does not identify a moment — “9am on 3 March” is a different moment in Warsaw and in Denver. Instant is a moment and does not tell you what any clock read.
Store timestamps as Instant. Store a scheduled local appointment as LocalDateTime plus a zone id, because the zone’s rules may change between now and then.
They are immutable
Every method returns a new object:
date.plusDays(1); // does nothing
date = date.plusDays(1); // correct
Which also means they are safe to share between threads, unlike the classes they replaced.
Formatting and parsing
DateTimeFormatter fmt = DateTimeFormatter.ofPattern("dd/MM/yyyy");
String s = date.format(fmt);
LocalDate back = LocalDate.parse("28/08/2026", fmt);
For storage and interchange use DateTimeFormatter.ISO_LOCAL_DATE or the default toString, which is ISO 8601 — sorts correctly as text and is unambiguous internationally. Reserve custom patterns for display.
Two pattern letters people confuse: mm is minutes and MM is months; yyyy is the calendar year and YYYY is the week-based year, which differs from it for a few days each January and produces a bug that appears once a year.
Why not Date and Calendar
java.util.Date is a timestamp badly named as a date, it is mutable, and it has deprecated methods with confusing semantics. Calendar numbers months from zero, so January is 0 — a defect that has caused more off-by-one errors than any other API in the standard library.
Both remain for compatibility. If you meet them at a boundary, convert immediately:
Instant i = legacyDate.toInstant();
Date back = Date.from(instant);