Dates and times
Java handles dates and times with the java.time package. Its classes are immutable: every operation returns a new object and the original stays unchanged. You'll meet the old Date and Calendar classes in older code, but new code doesn't use them.
LocalDate
LocalDate is a date without a time or time zone: year, month, day. You create one with of, and now() gives today's date:
void main() {
LocalDate birthday = LocalDate.of(1995, 3, 14);
IO.println(birthday);
IO.println(birthday.getYear());
IO.println(birthday.getMonth());
IO.println(birthday.getDayOfWeek());
}Output:
1995-03-14
1995
MARCH
TUESDAYMonths are numbered the human way — from 1 (January), not from zero. By default a date is printed in ISO format: year-month-day.
Time, and date with time
LocalTime holds only a time, LocalDateTime a date and a time together:
void main() {
LocalTime opening = LocalTime.of(9, 30);
LocalDateTime meeting = LocalDateTime.of(2026, 10, 5, 14, 0);
IO.println(opening);
IO.println(meeting);
IO.println(meeting.toLocalDate());
}Output:
09:30
2026-10-05T14:00
2026-10-05The current moment comes from LocalDate.now(), LocalTime.now() and LocalDateTime.now(). Their result naturally changes with every run, so the examples in this lesson use fixed dates.
Date arithmetic
Methods such as plusDays, minusMonths and plusWeeks return a new date. Java takes care of month lengths and leap years:
void main() {
LocalDate date = LocalDate.of(2024, 1, 31);
IO.println(date.plusDays(1));
IO.println(date.plusMonths(1));
IO.println(date.minusYears(1));
IO.println(date.isLeapYear());
}Output:
2024-02-01
2024-02-29
2023-01-31
trueJanuary 31 plus one month is February 29: there's no February 31, so Java takes the last day of the month.
How much time has passed
To compute the difference between dates, use ChronoUnit — it gives a number in the units you need. Period splits the difference into years, months and days, while Duration measures time in hours, minutes and seconds:
void main() {
LocalDate start = LocalDate.of(2026, 1, 15);
LocalDate end = LocalDate.of(2026, 10, 4);
IO.println(ChronoUnit.DAYS.between(start, end) + " days");
Period period = Period.between(start, end);
IO.println(period.getMonths() + " months " + period.getDays() + " days");
Duration lesson = Duration.between(LocalTime.of(10, 0), LocalTime.of(11, 35));
IO.println(lesson.toMinutes() + " minutes");
}Output:
262 days
8 months 19 days
95 minutesisBefore, isAfter and isEqual help compare dates.
Time zones
A LocalDateTime doesn't know which city an event happens in. For that there is ZonedDateTime — a date and time in a specific time zone. The withZoneSameInstant method shows the same moment in another zone:
void main() {
ZonedDateTime call = ZonedDateTime.of(
LocalDateTime.of(2026, 10, 5, 18, 0), ZoneId.of("Europe/Berlin"));
IO.println(call);
IO.println(call.withZoneSameInstant(ZoneId.of("Asia/Tokyo")));
}Output:
2026-10-05T18:00+02:00[Europe/Berlin]
2026-10-06T01:00+09:00[Asia/Tokyo]Timestamps in logs and databases use Instant — a point on the time line in UTC, without a zone.
Common mistake
java.time objects are immutable, so calling plusDays without assigning the result changes nothing:
void main() {
LocalDate deadline = LocalDate.of(2026, 10, 4);
deadline.plusDays(7);
IO.println(deadline);
}Output:
2026-10-04There's no compile error, but the result is lost. The fix is to keep the new date: deadline = deadline.plusDays(7);.