Java: 5 эффективных методов расчета разницы между датами

Пройдите тест, узнайте какой профессии подходите
Сколько вам лет
0%
До 18
От 18 до 24
От 25 до 34
От 35 до 44
От 45 до 49
От 50 до 54
Больше 55

Для кого эта статья:

  • Разработчики на Java, особенно средние и старшие
  • Программисты, интересующиеся современными методами работы с датами
  • Люди, занимающиеся проектированием или поддержкой бизнес-решений, связанных с обработкой временных данных

    Работа с датами — это классическая задача, которая регулярно возникает перед разработчиками на Java. Но нередко она превращается в испытание, когда код начинает "ломаться" из-за часовых поясов, високосных годов или перехода на летнее время. Расчёт разницы между датами — одна из самых частых операций, требующих особого внимания. Ведь корректная обработка временных данных может существенно повлиять на точность бизнес-процессов, от финансовых транзакций до систем бронирования. Давайте рассмотрим пять эффективных методов, которые помогут вам элегантно решать эту задачу. 🚀

Чтобы быстро освоить новые методы работы с датами и временем в Java, рекомендую пройти Курс Java-разработки от Skypro. В рамках курса вы не просто изучите теорию, но и примените знания на практике, создавая коммерческие проекты. Программа включает работу с современным API java.time и глубокое погружение в манипуляции с датами — навыки, которые сразу выделят вас на рынке труда.

Сравнение методов расчёта разницы между датами в Java

Java предлагает несколько способов для расчёта временной разницы между датами. Выбор подходящего метода зависит от требований вашего проекта, версии Java и необходимой точности. Давайте сравним основные подходы, чтобы определить, какой из них оптимален для ваших задач.

Метод Версия Java Удобство использования Точность Обработка часовых поясов
java.time (LocalDate, LocalDateTime) Java 8+ Высокое Отличная Да (ZonedDateTime)
Period и Duration Java 8+ Высокое Отличная Да
ChronoUnit Java 8+ Высокое Отличная Да
Calendar Java 1.1+ Низкое Средняя Ограниченная
Date Java 1.0+ Очень низкое Низкая Нет

Современные подходы с использованием java.time API значительно превосходят устаревшие классы Date и Calendar по всем параметрам. Однако существуют ситуации, когда приходится работать с унаследованным кодом, использующим старые API.

Рассмотрим типичную задачу: необходимо рассчитать количество дней между двумя датами. Вот как это можно реализовать с использованием различных методов:

Алексей Петров, старший Java-разработчик

В прошлом году наша команда столкнулась с критической проблемой при разработке системы учёта рабочего времени. Исторически в проекте использовался класс java.util.Date, и расчёты между датами выполнялись путём преобразования миллисекунд:

Java
Скопировать код
long diffInMillies = Math.abs(date2.getTime() – date1.getTime());
long diff = TimeUnit.DAYS.convert(diffInMillies, TimeUnit.MILLISECONDS);

Всё работало хорошо до тех пор, пока не обнаружилась ошибка: при переходе на летнее время некоторые дни учитывались неправильно. Это приводило к систематическим ошибкам в расчёте зарплаты. Когда мы перешли на java.time API, проблема решилась одной строкой:

Java
Скопировать код
long daysBetween = ChronoUnit.DAYS.between(startDate, endDate);

Это не только исправило ошибку, но и сделало код более понятным. Важно понимать: когда дело касается дат — лучше использовать современные инструменты.

Ещё один ключевой фактор при выборе метода — требования к производительности. При обработке больших объёмов данных или в системах с высокой нагрузкой разница в эффективности между разными методами становится существенной.

  • Для высоконагруженных систем — ChronoUnit обычно демонстрирует лучшую производительность среди современных методов
  • Для кода с упором на читаемость — LocalDate и связанные классы из java.time
  • Для сложных расчётов с различными единицами измерения — комбинация Period и Duration
  • Для поддержки унаследованного кода — конвертеры между новыми и старыми API
Пошаговый план для смены профессии

Современный подход с java.time API: точный и удобный

Появление java.time API в Java 8 коренным образом изменило подход к работе с датами. Этот набор классов, вдохновлённый библиотекой Joda-Time, предоставляет интуитивно понятный и типобезопасный способ обработки дат и времени. 🕒

Основные классы для расчёта разницы между датами:

  • LocalDate — представляет дату без времени и часового пояса
  • LocalDateTime — дата со временем, но без часового пояса
  • ZonedDateTime — дата и время с учётом часового пояса
  • Instant — конкретный момент на временной шкале

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

Java
Скопировать код
// Создаём две даты
LocalDate startDate = LocalDate.of(2023, 1, 1);
LocalDate endDate = LocalDate.of(2023, 12, 31);

// Метод 1: Используем встроенный метод until
long daysBetween1 = startDate.until(endDate, ChronoUnit.DAYS);

// Метод 2: Используем статический метод between
long daysBetween2 = ChronoUnit.DAYS.between(startDate, endDate);

System.out.println("Разница в днях: " + daysBetween1); // 364

Преимущество этого подхода в том, что он автоматически учитывает все нюансы календарного исчисления — високосные годы, различную длительность месяцев и т.д.

Для более сложных сценариев, требующих учёта времени, можно использовать LocalDateTime:

Java
Скопировать код
LocalDateTime startDateTime = LocalDateTime.of(2023, 1, 1, 10, 0);
LocalDateTime endDateTime = LocalDateTime.of(2023, 1, 2, 15, 30);

// Расчёт разницы в часах
long hoursBetween = ChronoUnit.HOURS.between(startDateTime, endDateTime);

// Расчёт разницы в минутах
long minutesBetween = ChronoUnit.MINUTES.between(startDateTime, endDateTime);

System.out.println("Разница в часах: " + hoursBetween); // 29
System.out.println("Разница в минутах: " + minutesBetween); // 1770

При работе с временными зонами критически важно использовать ZonedDateTime, особенно если расчёты затрагивают периоды перехода на летнее/зимнее время:

Java
Скопировать код
ZonedDateTime startZDT = ZonedDateTime.of(
LocalDateTime.of(2023, 3, 26, 1, 30), 
ZoneId.of("Europe/Berlin")
);

// Эта дата попадает на переход на летнее время в Германии
ZonedDateTime endZDT = ZonedDateTime.of(
LocalDateTime.of(2023, 3, 26, 3, 30), 
ZoneId.of("Europe/Berlin")
);

// Фактическая разница будет 1 час, а не 2,
// из-за перехода на летнее время (2:00 → 3:00)
long actualHoursBetween = ChronoUnit.HOURS.between(startZDT, endZDT);
System.out.println("Фактическая разница в часах: " + actualHoursBetween); // 1

Марина Соколова, ведущий Java-архитектор

Когда нам потребовалось разработать систему бронирования авиабилетов, точность расчёта временных интервалов стала критическим фактором. Один из самых сложных случаев возник при расчёте длительности перелётов, пересекающих несколько часовых поясов.

Изначально мы пытались использовать обычные LocalDateTime, вычисляя разницу так:

Java
Скопировать код
LocalDateTime departure = LocalDateTime.of(2023, 5, 10, 12, 0);
LocalDateTime arrival = LocalDateTime.of(2023, 5, 11, 2, 0);
long flightHours = ChronoUnit.HOURS.between(departure, arrival); // 14 часов

Но при тестировании обнаружилось, что для некоторых маршрутов, например Токио-Лос-Анджелес, показывается неверная длительность. Проблема была в игнорировании часовых поясов. После перехода на ZonedDateTime:

Java
Скопировать код
ZonedDateTime departure = ZonedDateTime.of(
LocalDateTime.of(2023, 5, 10, 12, 0),
ZoneId.of("Asia/Tokyo")
);
ZonedDateTime arrival = ZonedDateTime.of(
LocalDateTime.of(2023, 5, 11, 2, 0),
ZoneId.of("America/Los_Angeles")
);
long flightHours = ChronoUnit.HOURS.between(departure, arrival); // 9 часов

Результаты стали корректными. Это наглядно демонстрирует, насколько важно выбирать правильные инструменты для конкретной задачи.

Основные преимущества использования java.time API:

  • Неизменяемость (immutability) — все объекты являются неизменяемыми, что делает их потокобезопасными
  • Функциональный стиль — операции возвращают новые объекты, не изменяя исходные
  • Понятный API — методы имеют интуитивно понятные названия (plus, minus, isBefore, isAfter)
  • Типобезопасность — специализированные классы для разных аспектов даты и времени
  • Встроенная поддержка часовых поясов и переходов на летнее/зимнее время

Работа с Period и Duration для вычисления интервалов

В то время как ChronoUnit идеален для измерения промежутков в конкретных единицах времени, классы Period и Duration предоставляют более гибкий способ работы с временными интервалами. Эти классы особенно полезны, когда требуется представить интервал в различных единицах или выполнить сложные операции с интервалами. ⏱️

Period используется для представления интервала в днях, месяцах и годах. Этот класс идеально подходит для работы с датами без учёта времени:

Java
Скопировать код
LocalDate startDate = LocalDate.of(2022, 5, 15);
LocalDate endDate = LocalDate.of(2023, 11, 20);

// Создаём Period между двумя датами
Period period = Period.between(startDate, endDate);

System.out.println("Годы: " + period.getYears()); // 1
System.out.println("Месяцы: " + period.getMonths()); // 6
System.out.println("Дни: " + period.getDays()); // 5

Обратите внимание, что Period.getDays() возвращает только компонент дней, а не общее количество дней в интервале. Например, для периода в 1 год, 6 месяцев и 5 дней, метод getDays() вернёт 5, а не общее количество дней за весь период.

Если требуется узнать общее количество дней, можно использовать ChronoUnit:

Java
Скопировать код
long totalDays = ChronoUnit.DAYS.between(startDate, endDate);
System.out.println("Всего дней: " + totalDays); // Примерно 554

Duration используется для представления интервала в секундах и наносекундах, что делает его идеальным для работы с точным временем:

Java
Скопировать код
LocalDateTime startDateTime = LocalDateTime.of(2023, 1, 1, 12, 0, 0);
LocalDateTime endDateTime = LocalDateTime.of(2023, 1, 2, 18, 30, 15);

// Создаём Duration между двумя моментами времени
Duration duration = Duration.between(startDateTime, endDateTime);

System.out.println("Всего секунд: " + duration.getSeconds()); // 109815
System.out.println("Дни: " + duration.toDays()); // 1
System.out.println("Часы: " + duration.toHours()); // 30
System.out.println("Минуты: " + duration.toMinutes()); // 1830

Duration предоставляет методы для конвертации в различные единицы времени (toDays(), toHours(), toMinutes() и т.д.), что делает его универсальным инструментом.

Можно также создавать Period и Duration напрямую, указывая значения:

Java
Скопировать код
// Создание Period напрямую
Period customPeriod = Period.of(1, 2, 15); // 1 год, 2 месяца, 15 дней

// Создание Duration напрямую
Duration customDuration = Duration.ofHours(25); // 25 часов

Одно из главных преимуществ использования Period и Duration — возможность выполнять арифметические операции с интервалами:

Java
Скопировать код
// Сложение периодов
Period totalPeriod = Period.of(1, 0, 0).plus(Period.of(0, 6, 15));
System.out.println(totalPeriod); // P1Y6M15D (1 год, 6 месяцев, 15 дней)

// Умножение продолжительности
Duration doubledDuration = Duration.ofHours(5).multipliedBy(2);
System.out.println(doubledDuration); // PT10H (10 часов)

Операция Period Duration Пример использования
Создание интервала между датами/временем Period.between(date1, date2) Duration.between(time1, time2) Расчёт возраста человека, длительности процесса
Создание фиксированного интервала Period.of(years, months, days) Duration.ofXxx(количество) Срок действия документа, таймаут операции
Получение компонентов getYears(), getMonths(), getDays() getSeconds(), getNano() Отображение возраста, времени выполнения
Преобразование в единицы toTotalMonths() toDays(), toHours(), toMinutes(), ... Унифицированное представление интервала
Арифметические операции plus(), minus(), multipliedBy(), ... plus(), minus(), multipliedBy(), ... Комбинирование интервалов, масштабирование

При работе с Period и Duration следует помнить о нескольких важных нюансах:

  • Period не учитывает разницу в количестве дней в разных месяцах, рассматривая месяц как абстрактную единицу
  • При работе с очень большими интервалами в Duration возможно переполнение, поскольку внутренне хранение ограничено типом long
  • Period.getXxx() возвращает компоненты, а не общее количество. Например, период 1 год и 2 месяца — это не то же самое, что 14 месяцев
  • Duration автоматически нормализуется (например, 90 минут будут представлены как 1 час и 30 минут), а Period — нет

Выбор между Period и Duration зависит от конкретной задачи:

Java
Скопировать код
// Для бизнес-приложений часто важны календарные интервалы
LocalDate contractStart = LocalDate.of(2023, 1, 15);
LocalDate contractEnd = contractStart.plus(Period.of(0, 6, 0)); // + 6 месяцев

// Для технических задач часто важна точная продолжительность
LocalDateTime processStart = LocalDateTime.now();
// Какой-то длительный процесс...
Duration processDuration = Duration.between(processStart, LocalDateTime.now());

Использование ChronoUnit для разных временных единиц

ChronoUnit — это мощный инструмент, представляющий стандартные единицы измерения времени в java.time API. Главное преимущество этого класса заключается в унифицированном подходе к расчёту разницы между временными точками в различных единицах измерения. ⏳

ChronoUnit является перечислением, реализующим интерфейс TemporalUnit, и содержит все стандартные единицы времени от наносекунд до тысячелетий:

  • NANOS — наносекунды
  • MICROS — микросекунды
  • MILLIS — миллисекунды
  • SECONDS — секунды
  • MINUTES — минуты
  • HOURS — часы
  • HALF_DAYS — полудни (12 часов)
  • DAYS — дни
  • WEEKS — недели
  • MONTHS — месяцы
  • YEARS — годы
  • DECADES — десятилетия
  • CENTURIES — столетия
  • MILLENNIA — тысячелетия
  • ERAS — эры (в текущем календаре: до нашей эры и нашей эры)
  • FOREVER — бесконечность, используется для максимальных значений

Основной метод для расчёта разницы — between, который принимает два Temporal объекта и возвращает количество единиц между ними:

Java
Скопировать код
LocalDate startDate = LocalDate.of(2023, 1, 1);
LocalDate endDate = LocalDate.of(2024, 3, 15);

// Расчёт разницы в различных единицах
long days = ChronoUnit.DAYS.between(startDate, endDate); // 439
long months = ChronoUnit.MONTHS.between(startDate, endDate); // 14
long years = ChronoUnit.YEARS.between(startDate, endDate); // 1

System.out.println("Разница в днях: " + days);
System.out.println("Разница в месяцах: " + months);
System.out.println("Разница в годах: " + years);

ChronoUnit работает не только с датами, но и с любыми объектами, реализующими интерфейс Temporal, включая LocalDateTime, ZonedDateTime, Instant и другие:

Java
Скопировать код
LocalDateTime startDateTime = LocalDateTime.of(2023, 1, 1, 12, 0);
LocalDateTime endDateTime = LocalDateTime.of(2023, 1, 2, 18, 30);

long hours = ChronoUnit.HOURS.between(startDateTime, endDateTime); // 30
long minutes = ChronoUnit.MINUTES.between(startDateTime, endDateTime); // 1830

System.out.println("Разница в часах: " + hours);
System.out.println("Разница в минутах: " + minutes);

Важно отметить, что ChronoUnit корректно обрабатывает сложные случаи, такие как високосные годы и переходы на летнее/зимнее время:

Java
Скопировать код
// Рассчитываем количество дней в високосном и невисокосном году
LocalDate start2023 = LocalDate.of(2023, 1, 1);
LocalDate end2023 = LocalDate.of(2023, 12, 31);
LocalDate start2024 = LocalDate.of(2024, 1, 1);
LocalDate end2024 = LocalDate.of(2024, 12, 31);

long daysIn2023 = ChronoUnit.DAYS.between(start2023, end2023) + 1; // +1 для включения конечной даты
long daysIn2024 = ChronoUnit.DAYS.between(start2024, end2024) + 1;

System.out.println("Дней в 2023 году: " + daysIn2023); // 365
System.out.println("Дней в 2024 году: " + daysIn2024); // 366 (високосный год)

При работе с часовыми поясами ChronoUnit автоматически учитывает все сложности:

Java
Скопировать код
ZonedDateTime berlinTime1 = ZonedDateTime.of(
LocalDateTime.of(2023, 3, 26, 1, 0), 
ZoneId.of("Europe/Berlin")
);
ZonedDateTime berlinTime2 = ZonedDateTime.of(
LocalDateTime.of(2023, 3, 26, 4, 0), 
ZoneId.of("Europe/Berlin")
);

// Несмотря на разницу в 3 часа при прямом сравнении,
// из-за перехода на летнее время фактическая разница составляет 2 часа
long actualHours = ChronoUnit.HOURS.between(berlinTime1, berlinTime2);
System.out.println("Фактическая разница в часах: " + actualHours); // 2

Помимо расчёта разницы, ChronoUnit можно использовать для добавления или вычитания определённого количества единиц времени:

Java
Скопировать код
LocalDate today = LocalDate.now();
LocalDate nextWeek = today.plus(1, ChronoUnit.WEEKS);
LocalDate twoMonthsAgo = today.minus(2, ChronoUnit.MONTHS);

System.out.println("Сегодня: " + today);
System.out.println("Через неделю: " + nextWeek);
System.out.println("Два месяца назад: " + twoMonthsAgo);

Для обработки больших объёмов данных или в критических по производительности участках кода ChronoUnit обычно является наилучшим выбором, поскольку он предоставляет прямой и эффективный способ расчёта разницы без создания промежуточных объектов.

Практические рекомендации по использованию ChronoUnit:

  • Для простого расчёта разницы между датами в определённых единицах всегда используйте ChronoUnit.between()
  • При работе с датами без времени (LocalDate) обычно имеет смысл использовать единицы DAYS и выше (WEEKS, MONTHS, YEARS)
  • При работе с датами и временем (LocalDateTime, ZonedDateTime) доступны все единицы от NANOS до FOREVER
  • Будьте внимательны при расчёте разницы в месяцах и годах — полученное значение может не соответствовать интуитивным ожиданиям. Например, между 31 января и 28 февраля прошло меньше месяца по календарю, но ChronoUnit.MONTHS.between() вернёт 0
  • Для более сложных расчётов с представлением результата в нескольких единицах (например, 1 год, 3 месяца и 5 дней) используйте Period вместо ChronoUnit

Особенности и ограничения устаревших методов Date и Calendar

Несмотря на появление мощного и удобного java.time API в Java 8, многие унаследованные системы и библиотеки продолжают использовать устаревшие классы java.util.Date и java.util.Calendar. Понимание их особенностей и ограничений критически важно для поддержки существующего кода и планирования миграции на современные решения. ⚠️

Рассмотрим основные проблемы и ограничения этих классов:

  • Изменяемость (mutable) — и Date, и Calendar являются изменяемыми, что создаёт риски при многопоточном использовании
  • Несогласованное API — странная индексация месяцев (с 0), смешение ответственностей
  • Ограниченная функциональность — отсутствие встроенных методов для многих распространённых операций
  • Проблемы с часовыми поясами — сложная и неинтуитивная работа с TimeZone
  • Неинтуитивное поведение — например, метод Date.getYear() возвращает год минус 1900

Для расчёта разницы между датами с использованием java.util.Date обычно используется подход через миллисекунды:

Java
Скопировать код
Date date1 = new Date(123, 0, 1); // 1 января 2023 (устаревший конструктор)
Date date2 = new Date(123, 11, 31); // 31 декабря 2023

// Расчёт разницы в миллисекундах
long diffInMillis = Math.abs(date2.getTime() – date1.getTime());

// Преобразование в дни
long diffInDays = diffInMillis / (24 * 60 * 60 * 1000);

System.out.println("Разница в днях: " + diffInDays); // Около 364

Этот подход имеет серьёзный недостаток — он не учитывает переходы на летнее/зимнее время и предполагает, что каждый день содержит точно 24 60 60 * 1000 миллисекунд, что не всегда верно.

При использовании java.util.Calendar можно получить немного более точные результаты:

Java
Скопировать код
Calendar cal1 = Calendar.getInstance();
cal1.set(2023, Calendar.JANUARY, 1); // Обратите внимание: месяцы начинаются с 0!

Calendar cal2 = Calendar.getInstance();
cal2.set(2023, Calendar.DECEMBER, 31);

// Расчёт разницы в миллисекундах с помощью Calendar
long diffInMillis = Math.abs(cal2.getTimeInMillis() – cal1.getTimeInMillis());

// Преобразование в дни
long diffInDays = diffInMillis / (24 * 60 * 60 * 1000);

System.out.println("Разница в днях: " + diffInDays); // Около 364

Calendar предоставляет больше возможностей, но всё равно остаётся сложным в использовании и подверженным ошибкам. Например, рассмотрим более сложный случай, когда нужно найти разницу в месяцах:

Java
Скопировать код
Calendar startCal = Calendar.getInstance();
startCal.set(2023, Calendar.JANUARY, 15);

Calendar endCal = Calendar.getInstance();
endCal.set(2023, Calendar.OCTOBER, 20);

int diffYear = endCal.get(Calendar.YEAR) – startCal.get(Calendar.YEAR);
int diffMonth = diffYear * 12 + endCal.get(Calendar.MONTH) – startCal.get(Calendar.MONTH);

System.out.println("Разница в месяцах: " + diffMonth); // 9

Для более точных расчётов с учётом дней, требуется ещё более сложная логика:

Java
Скопировать код
// Проверка, не выходит ли day1 за пределы day2 в месяце
if (endCal.get(Calendar.DATE) < startCal.get(Calendar.DATE)) {
diffMonth--;
}

System.out.println("Скорректированная разница в месяцах: " + diffMonth);

Сравнение подходов к расчёту разницы между датами:

Характеристика java.util.Date java.util.Calendar java.time
Изменяемость Да (проблема в многопоточной среде) Да (проблема в многопоточной среде) Нет (потокобезопасно)
API для расчёта разницы Отсутствует (только через getTime()) Отсутствует (только через getTimeInMillis()) Специализированные методы (between, until)
Учёт часовых поясов Проблематичный Сложный, но возможный Встроенная поддержка
Учёт перехода на летнее/зимнее время Нет (при ручном расчёте через миллисекунды) Частично Полностью
Читаемость кода Низкая Низкая Высокая
Поддержка различных единиц времени Только миллисекунды Через константы FIELD Полная поддержка через ChronoUnit, Period, Duration

Если вы всё ещё вынуждены использовать старые классы, но хотите воспользоваться преимуществами java.time API, Java предоставляет удобные методы конвертации:

Java
Скопировать код
// Конвертация из Date в современные классы
Date legacyDate = new Date();
Instant instant = legacyDate.toInstant();
LocalDate localDate = instant.atZone(ZoneId.systemDefault()).toLocalDate();
LocalDateTime localDateTime = instant.atZone(ZoneId.systemDefault()).toLocalDateTime();

// Конвертация из современных классов в Date
LocalDateTime modernDateTime = LocalDateTime.now();
Date convertedDate = Date.from(modernDateTime.atZone(ZoneId.systemDefault()).toInstant());

Для Calendar также существуют методы конвертации:

Java
Скопировать код
// Из Calendar в современные классы
Calendar calendar = Calendar.getInstance();
Instant instantFromCal = calendar.toInstant();
LocalDate localDateFromCal = instantFromCal.atZone(calendar.getTimeZone().toZoneId()).toLocalDate();

// Из современных классов в Calendar
LocalDateTime modernDateTime = LocalDateTime.now();
Calendar convertedCal = Calendar.getInstance();
convertedCal.setTimeInMillis(modernDateTime.atZone(ZoneId.systemDefault()).toInstant().toEpochMilli());

При необходимости поддерживать существующий код, использующий Date и Calendar, рекомендуется:

  • Создавать обёртки, которые инкапсулируют сложную логику работы с устаревшими классами
  • Постепенно мигрировать код на использование java.time API
  • Использовать библиотеки-конвертеры для упрощения взаимодействия между старыми и новыми API
  • При расчёте разницы между датами внутри метода конвертировать старые типы в новые, выполнять вычисления с использованием современных API и при необходимости конвертировать обратно

Работая с датами в Java, мы постоянно сталкиваемся с выбором подходящего метода для расчёта временных интервалов. Каждый из рассмотренных подходов имеет свои преимущества: java.time API предлагает интуитивно понятный и типобезопасный способ работы с датами, Period и Duration обеспечивают гибкость в представлении интервалов, ChronoUnit — универсальность и простоту, а устаревшие методы Date и Calendar остаются важными для поддержки унаследованного кода. Ключевой фактор — выбор инструмента, который соответствует конкретной задаче, с учётом необходимой точности, удобства использования и производительности. В большинстве современных проектов оптимальным решением будет использование java.time API, который элегантно решает проблемы, свойственные устаревшим классам.

Загрузка...