Работа с датами и временем в Java: продвинутые методы и API

Онлайн-тренажер Python для начинающих

Изучайте Python легко и без перегрузки теорией. Решайте практические задачи с автоматической проверкой, получайте подсказки на русском языке и пишите код прямо в браузере — без необходимости что-либо устанавливать.

Начать курс

Продвинутая работа с датами и временем в Java: от основ к профессионализму

Работа с датами и временем — одна из ключевых задач любого enterprise-приложения. В Java долгое время стандартом был громоздкий java.util.Date и java.util.Calendar, но с выходом Java 8 ситуация кардинально изменилась. Новый java.time API (JSR-310) предложил разработчикам мощный, потокобезопасный и интуитивно понятный инструментарий. В этой статье мы разберем продвинутые техники работы с датами и временем, которые пригодятся каждому Java-разработчику.

1. Основы java.time API: LocalDate, LocalTime, LocalDateTime

Первый шаг к профессиональной работе — понимание трех базовых классов, которые заменили старые аналоги:

  • LocalDate — дата без времени (год, месяц, день).
  • LocalTime — время без даты (часы, минуты, секунды, наносекунды).
  • LocalDateTime — объединение даты и времени без привязки к часовому поясу.
// Получение текущей даты и времени
LocalDate today = LocalDate.now(); // 2025-03-21
LocalTime now = LocalTime.now(); // 14:30:15.123
LocalDateTime current = LocalDateTime.now(); // 2025-03-21T14:30:15.123

// Создание конкретной даты
LocalDate birthDate = LocalDate.of(1990, Month.JUNE, 15);
LocalTime meetingTime = LocalTime.of(10, 30, 0);

// Манипуляции с датами
LocalDate nextWeek = today.plusWeeks(1);
LocalDate lastMonth = today.minusMonths(1);

Эти классы являются иммутабельными — любые операции возвращают новый объект. Это гарантирует потокобезопасность и предсказуемость кода.

2. Работа с часовыми поясами: ZonedDateTime и ZoneId

Глобальные приложения не могут игнорировать временные зоны. Для этого в java.time предусмотрен класс ZonedDateTime.

// Текущее время в UTC
ZonedDateTime utcNow = ZonedDateTime.now(ZoneOffset.UTC);

// Время в конкретном часовом поясе
ZoneId moscowZone = ZoneId.of("Europe/Moscow");
ZonedDateTime moscowTime = ZonedDateTime.now(moscowZone);

// Конвертация между зонами
ZonedDateTime nyTime = moscowTime.withZoneSameInstant(ZoneId.of("America/New_York"));

// Получение смещения
ZoneOffset offset = moscowTime.getOffset(); // +03:00

Совет профессионала: всегда храните даты в UTC на сервере и конвертируйте в локальный часовой пояс только на уровне представления (UI). Это избавит от проблем с переходом на летнее/зимнее время.

3. Парсинг и форматирование дат: DateTimeFormatter

Одна из самых частых задач — преобразование строки в дату и обратно. DateTimeFormatter предоставляет гибкие возможности.

Базовое форматирование

DateTimeFormatter formatter = DateTimeFormatter.ofPattern("dd.MM.yyyy HH:mm:ss");
LocalDateTime dateTime = LocalDateTime.parse("21.03.2025 14:30:00", formatter);
String formatted = dateTime.format(formatter); // "21.03.2025 14:30:00"

Продвинутые шаблоны

// ISO-8601
DateTimeFormatter isoFormatter = DateTimeFormatter.ISO_LOCAL_DATE_TIME;
String isoDate = dateTime.format(isoFormatter); // "2025-03-21T14:30:00"

// Локализованное форматирование
DateTimeFormatter italianFormatter = DateTimeFormatter.ofPattern("d MMMM yyyy", Locale.ITALY);
String italianDate = dateTime.format(italianFormatter); // "21 marzo 2025"

// Свой формат с текстовыми полями
DateTimeFormatter customFormatter = new DateTimeFormatterBuilder()
    .appendValue(ChronoField.YEAR)
    .appendLiteral(" год, ")
    .appendText(ChronoField.MONTH_OF_YEAR, TextStyle.FULL)
    .appendLiteral(" ")
    .appendValue(ChronoField.DAY_OF_MONTH)
    .toFormatter();
String custom = dateTime.format(customFormatter); // "2025 год, марта 21"

Важно помнить: DateTimeFormatter также потокобезопасен (в отличие от SimpleDateFormat), поэтому его можно объявлять как статическую константу.

4. Продвинутые вычисления: Duration, Period и ChronoUnit

Для расчета разницы между датами и временем используйте специализированные классы:

  • Duration — для времени (секунды, наносекунды), подходит для LocalTime и LocalDateTime.
  • Period — для дат (годы, месяцы, дни), работает с LocalDate.
  • ChronoUnit — универсальный перечислитель временных единиц.
// Разница между двумя датами
LocalDate start = LocalDate.of(2024, 1, 1);
LocalDate end = LocalDate.of(2025, 3, 21);
Period period = Period.between(start, end);
System.out.println("Прошло: " + period.getYears() + " лет, " 
    + period.getMonths() + " месяцев, " + period.getDays() + " дней");

// Разница в часах между двумя моментами
LocalDateTime startTime = LocalDateTime.of(2025, 3, 21, 10, 0);
LocalDateTime endTime = LocalDateTime.of(2025, 3, 21, 18, 30);
Duration duration = Duration.between(startTime, endTime);
long hours = duration.toHours(); // 8
long minutes = duration.toMinutes() % 60; // 30

// Использование ChronoUnit
long daysBetween = ChronoUnit.DAYS.between(start, end); // 445
long weeksBetween = ChronoUnit.WEEKS.between(start, end); // 63

Работа с интервалами и временными корректировщиками

// TemporalAdjuster — мощный инструмент для манипуляций
LocalDate firstDayOfMonth = today.with(TemporalAdjusters.firstDayOfMonth());
LocalDate nextMonday = today.with(TemporalAdjusters.next(DayOfWeek.MONDAY));
LocalDate lastDayOfYear = today.with(TemporalAdjusters.lastDayOfYear());

// Свой TemporalAdjuster — следующий рабочий день
TemporalAdjuster nextWorkDay = temporal -> {
    LocalDate date = LocalDate.from(temporal);
    do {
        date = date.plusDays(1);
    } while (date.getDayOfWeek() == DayOfWeek.SATURDAY 
             || date.getDayOfWeek() == DayOfWeek.SUNDAY                

Блоги

Рекомендаций по книгам