Функции PHP и Java возвращают разные даты при расчете на 6 месяцев вперед

У меня есть следующий код, чтобы вычислить, какой день будет через 6 месяцев с сегодняшнего дня.

// Java code
Date currentDate = (new SimpleDateFormat("yyyy-MM-dd")).parse("2024-08-30");
        
Calendar calendar = Calendar.getInstance();
calendar.setTime(currentDate);
calendar.add(Calendar.MONTH, 6);
Date sixMonthsLaterDate = calendar.getTime();
String sixMonthsLaterDateString = new SimpleDateFormat("yyyy-MM-dd").format(sixMonthsLaterDate);

System.out.println("sixMonthsLaterDateString: " + sixMonthsLaterDateString); // returns 2025-02-28

в Java он возвращает «28 февраля 2025 г.»

// PHP code
$currentDate = date_create_from_format('Y-m-d', '2024-08-30');
$sixMonthsLaterDate = $currentDate->modify('+6 month');
$sixMonthsLaterDateString = date_format($sixMonthsLaterDate, 'Y-m-d');
echo "sixMonthsLaterDateString: $sixMonthsLaterDateString"; // returns 2025-03-02

в PHP он возвращает «2025-03-02»

Почему они разные? Может ли кто-нибудь объяснить это? Спасибо!

🤔 А знаете ли вы, что...
Java имеет множество библиотек и фреймворков, упрощающих разработку.


3
125
1

Ответ:

Решено

вр; доктор

LocalDate.parse( "2024-08-30" ).plusMonths( 6 )  // Accounts for calendar months, adjusting to end-of-month if needed.

Посмотрите этот код, запущенный на Ideone.com.

2025-02-28

Избегайте устаревших классов даты и времени

Вы используете ужасно ошибочные классы даты и времени, которые теперь устарели. Много лет назад они были вытеснены современными классами java.time, определенными в JSR 310.

LocalDate

Для значения только даты используйте java.time.LocalDate.

LocalDate ld = LocalDate.parse( "2024-08-30" ) ;

Укажите полгода с классом Period.

Period period = Period.ofMonths( 6 ) ;

Добавлять.

LocalDate sixMonthsLater = ld.plus( period ) ;

Объясните отличие от PHP

Как обсуждалось в комментариях, подсчет месяцев — сложная тема. В современную эпоху в календарной системе ISO 8601 месяцы имеют разную длину: 28, 29, 30 и 31 день.

Java-подход

Платформа java.time пытается учитывать календарный месяц, а не добавляет произвольное количество дней, например ( 6 * 30 ).

Алгоритм описан в Javadoc LocalDate#plusMonths. Цитирую:

… три шага:

  1. Добавьте введенные месяцы в поле месяца года.

  2. Проверьте, будет ли полученная дата недействительной.

  3. При необходимости откорректируйте день месяца на последний действительный день.

В случае ввода вашего примера 2024-08-30 шестым месяцем после августа является февраль. Затем мы рассматриваем день месяца. Но ни в одном феврале не бывает 30-го дня. Нет и дня 29 в феврале того года. Последний действительный день февраля — 28. Вуаля, 28 февраля 2025 г. — вот решение.

PHP-подход

Я понятия не имею, что делает ваша библиотека PHP. Не учитывается календарный месяц. Это не сложение 6*30 дней или 6*31 дней.

Этот комментарий C3roe дает объяснение поведения PHP, но я его не исследовал. Цитирую:

… PHP приземлился 30 февраля 2025 г., а затем попытался исправить «переполнение», переместив соответствующее количество дней в следующий месяц.

При таком подходе два дня «переполнения» — это несуществующие 29 и 30. Прибавив эти два дня к 28 февраля 2025 года, вы получите 2 марта.

Если это действительно алгоритм подхода PHP, мне это кажется странным. Это был бы уродливый гибридный подход, не учитывающий ни полностью календарные месяцы, ни точный подсчет дней. Этот подход учитывает «лишние» дни в последнем месяце (в данном случае февраль), но игнорирует разницу в продолжительности промежуточных месяцев (в данном случае сентябрь-январь).

Правильное решение

Мы видели следующие подходы к перемещению вперед/назад во времени по месяцам:

  • Произвольное количество дней, например (6 * 30).
  • Подсчет календарных месяцев с корректировкой на конец календарного месяца, если это необходимо (подход java.time).
  • Гибридный подход, при котором мы перемещаемся по календарным месяцам, игнорируя их продолжительность, но затем учитываем возможные дни «переполнения» в последнем месяце (похоже, подход PHP).

Могут существовать и другие подходы. Так какой же правильный подход использовать?

Правильный подход — это… все, что говорят руководители вашего проекта приложения. Вы всегда должны поднимать такие вопросы, связанные с датой и временем, с экспертами в предметной области вашего проекта. Это могут быть экспедиторы, координаторы логистики, бухгалтеры и т. д. Попросите их указать, как должен работать подсчет месяцев. Я бы рекомендовал, чтобы они сделали это в письменной форме. Затем скопируйте эти правила в комментарии вашей кодовой базы.


Интересные вопросы для изучения