Даты и время
Для дат и времени в Java есть пакет java.time. Его классы неизменяемые: любая операция возвращает новый объект, а исходный не меняется. Старые классы Date и Calendar вы встретите в давнем коде, но в новом их не используют.
LocalDate
LocalDate — дата без времени и часового пояса: год, месяц, день. Создать её можно методом of, а сегодняшнюю дату даёт now():
void main() {
LocalDate birthday = LocalDate.of(1995, 3, 14);
IO.println(birthday);
IO.println(birthday.getYear());
IO.println(birthday.getMonth());
IO.println(birthday.getDayOfWeek());
}Результат:
1995-03-14
1995
MARCH
TUESDAYМесяцы нумеруются по-человечески — с 1 (январь), а не с нуля. По умолчанию дата выводится в формате ISO: год-месяц-день.
Время и дата со временем
LocalTime хранит только время, LocalDateTime — дату и время вместе:
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());
}Результат:
09:30
2026-10-05T14:00
2026-10-05Текущий момент дают LocalDate.now(), LocalTime.now() и LocalDateTime.now(). Их результат, конечно, меняется при каждом запуске, поэтому в примерах урока используются фиксированные даты.
Арифметика дат
Методы plusDays, minusMonths, plusWeeks и подобные возвращают новую дату. Java сама учитывает длину месяцев и високосные годы:
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());
}Результат:
2024-02-01
2024-02-29
2023-01-31
true31 января плюс месяц — это 29 февраля: такого числа, как 31 февраля, нет, и Java берёт последний день месяца.
Сколько прошло
Чтобы посчитать разницу между датами, используют ChronoUnit — он выдаёт число в нужных единицах. Period раскладывает разницу на годы, месяцы и дни, а Duration измеряет время в часах, минутах и секундах:
void main() {
LocalDate start = LocalDate.of(2026, 1, 15);
LocalDate end = LocalDate.of(2026, 10, 4);
IO.println(ChronoUnit.DAYS.between(start, end) + " дн.");
Period period = Period.between(start, end);
IO.println(period.getMonths() + " мес. " + period.getDays() + " дн.");
Duration lesson = Duration.between(LocalTime.of(10, 0), LocalTime.of(11, 35));
IO.println(lesson.toMinutes() + " мин.");
}Результат:
262 дн.
8 мес. 19 дн.
95 мин.Сравнивать даты помогают isBefore, isAfter и isEqual.
Часовые пояса
LocalDateTime не знает, в каком городе происходит событие. Для этого есть ZonedDateTime — дата и время в конкретном часовом поясе. Метод withZoneSameInstant показывает тот же момент в другом поясе:
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")));
}Результат:
2026-10-05T18:00+02:00[Europe/Berlin]
2026-10-06T01:00+09:00[Asia/Tokyo]Для отметок времени в журналах и базах данных используют Instant — точку на шкале времени в UTC, без пояса.
Частая ошибка
Объекты java.time неизменяемы, поэтому вызов plusDays без присваивания ничего не меняет:
void main() {
LocalDate deadline = LocalDate.of(2026, 10, 4);
deadline.plusDays(7);
IO.println(deadline);
}Результат:
2026-10-04Ошибки компиляции нет, но результат потерян. Правильно — сохранить новую дату: deadline = deadline.plusDays(7);.