У меня есть следующий код, чтобы вычислить, какой день будет через 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 имеет множество библиотек и фреймворков, упрощающих разработку.
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 ) ;
Как обсуждалось в комментариях, подсчет месяцев — сложная тема. В современную эпоху в календарной системе ISO 8601 месяцы имеют разную длину: 28, 29, 30 и 31 день.
Платформа java.time пытается учитывать календарный месяц, а не добавляет произвольное количество дней, например ( 6 * 30 )
.
Алгоритм описан в Javadoc LocalDate#plusMonths. Цитирую:
… три шага:
Добавьте введенные месяцы в поле месяца года.
Проверьте, будет ли полученная дата недействительной.
При необходимости откорректируйте день месяца на последний действительный день.
В случае ввода вашего примера 2024-08-30
шестым месяцем после августа является февраль. Затем мы рассматриваем день месяца. Но ни в одном феврале не бывает 30-го дня. Нет и дня 29 в феврале того года. Последний действительный день февраля — 28. Вуаля, 28 февраля 2025 г. — вот решение.
Я понятия не имею, что делает ваша библиотека PHP. Не учитывается календарный месяц. Это не сложение 6*30 дней или 6*31 дней.
Этот комментарий C3roe дает объяснение поведения PHP, но я его не исследовал. Цитирую:
… PHP приземлился 30 февраля 2025 г., а затем попытался исправить «переполнение», переместив соответствующее количество дней в следующий месяц.
При таком подходе два дня «переполнения» — это несуществующие 29 и 30. Прибавив эти два дня к 28 февраля 2025 года, вы получите 2 марта.
Если это действительно алгоритм подхода PHP, мне это кажется странным. Это был бы уродливый гибридный подход, не учитывающий ни полностью календарные месяцы, ни точный подсчет дней. Этот подход учитывает «лишние» дни в последнем месяце (в данном случае февраль), но игнорирует разницу в продолжительности промежуточных месяцев (в данном случае сентябрь-январь).
Мы видели следующие подходы к перемещению вперед/назад во времени по месяцам:
Могут существовать и другие подходы. Так какой же правильный подход использовать?
Правильный подход — это… все, что говорят руководители вашего проекта приложения. Вы всегда должны поднимать такие вопросы, связанные с датой и временем, с экспертами в предметной области вашего проекта. Это могут быть экспедиторы, координаторы логистики, бухгалтеры и т. д. Попросите их указать, как должен работать подсчет месяцев. Я бы рекомендовал, чтобы они сделали это в письменной форме. Затем скопируйте эти правила в комментарии вашей кодовой базы.