1. Прибавление и вычитание дат и времени
В программировании работа с датами и временем — это не только "красиво вывести дату на экран". Очень часто требуется:
- узнать, сколько дней осталось до важного события;
- посчитать возраст пользователя;
- определить, сколько времени прошло между двумя событиями;
- проверить, не наступил ли дедлайн;
- прибавить к дате определённое количество дней, недель, месяцев или часов.
Например, в нашем приложении мы хотим напомнить пользователю, что до дедлайна задачи осталось 3 дня. Или поздравить его с прошедшим днём рождения (если дата уже в прошлом).
Для этого в java.time есть специальные классы и методы, которые делают такие вычисления простыми и безопасными.
Основные методы для сложения и вычитания
У всех классов даты и времени (LocalDate, LocalTime, LocalDateTime, ZonedDateTime, Instant) есть методы:
- plusXxx() — прибавить (дни, месяцы, годы, часы, минуты и т.д.)
- minusXxx() — вычесть (то же самое)
import java.time.LocalDate;
LocalDate today = LocalDate.now(); // Сегодняшняя дата
LocalDate tomorrow = today.plusDays(1); // Завтра
LocalDate nextMonth = today.plusMonths(1); // Через месяц
LocalDate lastWeek = today.minusWeeks(1); // Неделю назад
System.out.println("Сегодня: " + today);
System.out.println("Завтра: " + tomorrow);
System.out.println("Через месяц: " + nextMonth);
System.out.println("Неделю назад: " + lastWeek);
Все эти методы возвращают новый объект, а не изменяют исходный. Неизменяемость — наше всё!
import java.time.LocalTime;
LocalTime now = LocalTime.now();
LocalTime inTwoHours = now.plusHours(2);
LocalTime tenMinutesAgo = now.minusMinutes(10);
System.out.println("Сейчас: " + now);
System.out.println("Через 2 часа: " + inTwoHours);
System.out.println("10 минут назад: " + tenMinutesAgo);
Комбинированные методы
LocalDate vacation = today.plusMonths(2).plusDays(10);
System.out.println("Отпуск: " + vacation);
2. Получение разницы между датами: Period и Duration
Когда нужно узнать, сколько времени прошло между двумя датами или моментами времени, на сцену выходят два героя — Period и Duration. Давайте познакомимся с ними поближе.
В чём разница между Period и Duration?
- Period — для работы с датами (год, месяц, день). Например, "3 года, 2 месяца и 5 дней".
- Duration — для работы с временем (часы, минуты, секунды, наносекунды). Например, "5 часов 30 минут".
| Класс | Для чего предназначен | Пример использования |
|---|---|---|
|
Разница между датами | Сколько лет/месяцев/дней между датами |
|
Разница между моментами | Сколько часов/минут/секунд между событиями |
Пример: вычисляем возраст пользователя
import java.time.LocalDate;
import java.time.Period;
LocalDate birthday = LocalDate.of(2000, 1, 15); // День рождения
LocalDate today = LocalDate.now();
Period age = Period.between(birthday, today);
System.out.println("Возраст: " + age.getYears() + " лет, " +
age.getMonths() + " месяцев, " +
age.getDays() + " дней");
Аналогия: Period — это как "человеческая" разница между датами ("мне 24 года 5 месяцев 12 дней").
Пример: сколько времени осталось до дедлайна
import java.time.LocalDate;
LocalDate deadline = LocalDate.of(2025, 7, 1);
LocalDate today = LocalDate.now();
if (today.isBefore(deadline)) {
Period left = Period.between(today, deadline);
System.out.println("До дедлайна осталось: " +
left.getMonths() + " месяцев и " +
left.getDays() + " дней");
} else {
System.out.println("Дедлайн уже прошёл!");
}
Duration: разница во времени
import java.time.LocalDateTime;
import java.time.Duration;
LocalDateTime start = LocalDateTime.of(2025, 6, 1, 10, 0, 0);
LocalDateTime end = LocalDateTime.of(2025, 6, 1, 15, 30, 0);
Duration duration = Duration.between(start, end);
System.out.println("Продолжительность: " + duration.toHours() + " часов " +
(duration.toMinutes() % 60) + " минут");
Duration измеряет разницу в секундах, минутах, часах, днях (но дни — всегда по 24 часа, без учёта календарных особенностей).
Сравнение: когда использовать Period, а когда Duration?
- Если у вас только даты (без времени) — используйте Period.
- Если у вас есть время (например, LocalDateTime, Instant) — используйте Duration.
3. Сравнение дат и времени: isBefore, isAfter, equals
Иногда нужно просто понять: дата уже наступила или ещё нет? Событие в прошлом или будущем? Для этого у всех классов даты и времени есть методы:
- isBefore(другаяДата)
- isAfter(другаяДата)
- isEqual(другаяДата) (или просто equals)
import java.time.LocalDate;
LocalDate today = LocalDate.now();
LocalDate deadline = LocalDate.of(2025, 7, 1);
if (today.isBefore(deadline)) {
System.out.println("Всё ещё есть время!");
} else if (today.isEqual(deadline)) {
System.out.println("Сегодня дедлайн!");
} else {
System.out.println("Дедлайн уже прошёл :(");
}
import java.time.LocalTime;
LocalTime now = LocalTime.now();
LocalTime lunch = LocalTime.of(13, 0);
if (now.isAfter(lunch)) {
System.out.println("Обед уже был!");
} else {
System.out.println("До обеда ещё есть время.");
}
Важно: сравнивать можно только объекты одного типа (LocalDate с LocalDate, LocalDateTime с LocalDateTime и т.д.).
4. Практика: задачи на вычисление разницы и проверку сроков
Пример: сколько дней осталось до дедлайна задачи
import java.time.LocalDate;
import java.time.temporal.ChronoUnit;
LocalDate today = LocalDate.now();
LocalDate deadline = LocalDate.of(2025, 7, 1);
long daysLeft = ChronoUnit.DAYS.between(today, deadline);
if (daysLeft > 0) {
System.out.println("До дедлайна осталось " + daysLeft + " дней");
} else if (daysLeft == 0) {
System.out.println("Сегодня дедлайн!");
} else {
System.out.println("Дедлайн прошёл " + Math.abs(daysLeft) + " дней назад");
}
ChronoUnit.DAYS.between() — удобный способ узнать точное количество дней между двумя датами (без учёта месяцев и лет).
Пример: сколько секунд между двумя событиями
import java.time.LocalDateTime;
import java.time.temporal.ChronoUnit;
LocalDateTime start = LocalDateTime.of(2025, 6, 1, 10, 0, 0);
LocalDateTime end = LocalDateTime.of(2025, 6, 1, 15, 30, 0);
long seconds = ChronoUnit.SECONDS.between(start, end);
System.out.println("Между событиями прошло " + seconds + " секунд");
Пример: вычисление возраста задачи
import java.time.LocalDate;
import java.time.Period;
LocalDate created = LocalDate.of(2025, 5, 20);
LocalDate today = LocalDate.now();
Period period = Period.between(created, today);
System.out.println("Задача была создана " +
period.getDays() + " дней, " +
period.getMonths() + " месяцев и " +
period.getYears() + " лет назад.");
Пример: длительность между двумя Instant
import java.time.Instant;
import java.time.Duration;
Instant start = Instant.now();
// ... что-то делаем ...
Instant end = Instant.now();
Duration duration = Duration.between(start, end);
System.out.println("Выполнение заняло " + duration.toMillis() + " миллисекунд");
5. Особенности использования Period и Duration
Особенности Period
- Period считает разницу по календарю: между 28 февраля и 1 марта — 1 день, даже если это разные месяцы.
- Можно получить количество лет, месяцев, дней по отдельности: getYears(), getMonths(), getDays().
- Если нужно узнать общее количество дней между датами — используйте ChronoUnit.DAYS.between().
Особенности Duration
- Duration работает с точным временем (часы, минуты, секунды).
- Может работать с LocalTime, LocalDateTime, Instant.
- Для дат без времени (LocalDate) использовать нельзя — будет исключение.
Пример: разница между Period и Duration
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.Duration;
import java.time.Period;
LocalDate date1 = LocalDate.of(2025, 2, 28);
LocalDate date2 = LocalDate.of(2025, 3, 1);
Period period = Period.between(date1, date2); // 2 дня (високосный год)
long days = java.time.temporal.ChronoUnit.DAYS.between(date1, date2); // 2 дня
System.out.println("Period: " + period.getDays() + " дней");
System.out.println("ChronoUnit: " + days + " дней");
LocalDateTime dt1 = LocalDateTime.of(2025, 6, 1, 23, 0);
LocalDateTime dt2 = LocalDateTime.of(2025, 6, 2, 1, 0);
Duration duration = Duration.between(dt1, dt2); // 2 часа
System.out.println("Duration: " + duration.toHours() + " часов");
6. Использование Duration и Period для вывода разницы в удобном виде
Когда вы вычисляете разницу между датами или временем для пользователя, не всегда удобно показывать "123456 секунд". Обычно хочется что-то вроде "2 дня 3 часа 15 минут".
Пример: вывод разницы между датами и временем
import java.time.LocalDateTime;
import java.time.Duration;
LocalDateTime start = LocalDateTime.of(2025, 6, 1, 14, 0);
LocalDateTime end = LocalDateTime.of(2025, 6, 3, 16, 30);
Duration duration = Duration.between(start, end);
long days = duration.toDays();
long hours = duration.minusDays(days).toHours();
long minutes = duration.minusDays(days).minusHours(hours).toMinutes();
System.out.println("Разница: " + days + " дней " +
hours + " часов " + minutes + " минут");
7. Особенности при работе с ZonedDateTime и Instant
- Разница между двумя ZonedDateTime в разных зонах считается по абсолютному времени, а не по "человеческому" времени в каждой зоне.
- Переход на летнее/зимнее время может влиять на разницу в часах между датами.
Пример: разница между ZonedDateTime
import java.time.ZonedDateTime;
import java.time.ZoneId;
import java.time.Duration;
ZonedDateTime MinskTime = ZonedDateTime.of(2025, 6, 1, 12, 0, 0, 0, ZoneId.of("Europe/Minsk"));
ZonedDateTime nyTime = ZonedDateTime.of(2025, 6, 1, 5, 0, 0, 0, ZoneId.of("America/New_York"));
Duration diff = Duration.between(nyTime, MinskTime);
System.out.println("Разница: " + diff.toHours() + " часов");
8. Типичные ошибки при вычислениях с датами и временем
Ошибка №1: Путают Period и Duration.
Если попытаться использовать Duration для LocalDate, получите исключение. Для дат используйте Period, для времени — Duration.
Ошибка №2: Не учитывают часовые пояса.
Если сравнивать даты и время в разных часовых поясах, можно получить неожиданные результаты. Лучше приводить всё к одному поясу или использовать Instant.
Ошибка №3: Ожидают, что методы plus/minus изменяют объект.
Все объекты java.time неизменяемы! Не забывайте присваивать результат в новую переменную (например, today = today.plusDays(1)).
Ошибка №4: Используют getDays() у Period и думают, что это общее количество дней.
На самом деле это только остаточные дни после вычитания лет и месяцев. Для общего количества дней используйте ChronoUnit.DAYS.between().
Ошибка №5: Сравнивают объекты разных типов.
Методы isBefore, isAfter, isEqual работают только для объектов одного и того же типа (например, LocalDate с LocalDate).
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ