Добавление месяца к дате в Java двумя способами дает два разных результата (философия високосного года?) - PullRequest
0 голосов
/ 27 апреля 2018

Если предположить, что 31 января 2000 года - високосный год, следующие два способа добавления месяца дают разные результаты. Я делаю что-то не так или это две разные философии обработки високосных месяцев? И, если эти два подхода - просто философская разница, как вы узнаете, какой метод выбрать?

Метод 1: с использованием LocalDate plusMonths():

LocalDate atestDate = LocalDate.parse("2000-01-31");
System.out.println("One month in future using LocalDate.addMonths() " +   atestDate.plusMonths(1));

Выход:

One month in future using LocalDate.addMonths() 2000-02-29

Метод 2: с использованием Calendar:

Calendar zcal = Calendar.getInstance();
zcal.set(Calendar.DAY_OF_MONTH, 31);
zcal.set(Calendar.MONTH, 1);
zcal.set(Calendar.YEAR, 2000);
zcal.add(Calendar.MONTH, 0);
System.out.println("ONE MONTH IN FUTURE using Calendar: " 
 + zcal.getTime());

Выход:

ONE MONTH IN FUTURE using Calendar: Thu Mar 02 2000 

Почему выходные данные этих двух дат не совпадают?

Спасибо.

Ответы [ 3 ]

0 голосов
/ 28 апреля 2018

Проблема, с которой я столкнулся в публикации выше, оказалась опечаткой.

Calendar zcal = Calendar.getInstance();
zcal.set(Calendar.DAY_OF_MONTH, 31);
zcal.set(Calendar.MONTH, 1);
zcal.set(Calendar.YEAR, 2000);
zcal.add(Calendar.MONTH, 0);
System.out.println("ONE MONTH IN FUTURE using Calendar: " 
 + zcal.getTime());

Должно было быть:

Calendar zcal = Calendar.getInstance();
zcal.set(Calendar.DAY_OF_MONTH, 31);
zcal.set(Calendar.MONTH, Calendar.JANUARY);
zcal.set(Calendar.YEAR, 2000);
zcal.add(Calendar.MONTH, 1);
System.out.println("ONE MONTH IN FUTURE using Calendar: " 
 + zcal.getTime());

Тогда я получу ожидаемую дату совпадения.

Спасибо всем, кто ответил! :)

0 голосов
/ 28 апреля 2018

ТЛ; др

Просто используйте LocalDate и забудьте все о Calendar (неприятный унаследованный класс).

LocalDate.parse( "2000-01-31" ).plusMonths( 1 ).toString()

2000-02-29

Метод 1

Метод 1: использование LocalDate plusMonths ():

Документация класса LocalDate объясняет, что сначала добавляется номер месяца, а день месяца остается один. Если этот день месяца недействителен в этом месяце (29, 30 или 31), он корректируется назад к последнему действительному дню месяца.

Этот метод добавляет указанную сумму к полю месяцев в три этапа:

  1. Добавить введенные месяцы в поле месяца года
  2. Проверьте, будет ли полученная дата недействительной
  3. При необходимости настройте день месяца на последний действительный день.

Например, 2007-03-31 плюс один месяц приведут к неверной дате 2007-04-31. Вместо того, чтобы возвращать неверный результат, вместо этого выбирается последний действительный день месяца, 2007-04-30.

Похоже, разумный подход ко мне.

В вашем примере LocalDate сначала изменилось 2000-01-31 на 2000-02-31. В феврале нет 31-го, поэтому он вернулся к 30-му. Но не 30-го февраля. Так что он пошел еще дальше до 29. Бинго! В этом году в этом месяце действительно 29-й день, потому что 2000 год - это високосный год . Таким образом, ответ 2000-02-29.

LocalDate.parse( "2000-01-31" )
         .plusMonths( 1 )
         .toString()

2000-02-29

Метод 2

Способ 2: использование календаря:

Не беспокойтесь.

Этот ужасно неприятный старый класс Calendar теперь унаследован, полностью заменен классами java.time , как определено в JSR 310 . В частности, заменено на ZonedDateTime.

Никогда больше не трогай Calendar класс. Избавь себя от боли и головной боли и притворись, что этого класса никогда не было.

Кроме того, Calendar было значением даты со временем. Не подходит для значения только для даты, такого как ваше в Вопросе.

Преобразовать наследие <-> современный

Если вам необходимо взаимодействовать с кодом, который еще не обновлен до java.time , выполните преобразование между унаследованными классами и java.time , вызвав новые методы преобразования, добавленные в старые классы. , Для получения дополнительной информации см. Вопрос, Преобразование java.util.Date в тип «java.time»?


О java.time

Фреймворк java.time встроен в Java 8 и более поздние версии. Эти классы вытесняют проблемные старые устаревшие классы даты и времени, такие как java.util.Date, Calendar, & SimpleDateFormat.

Проект Joda-Time , теперь в режиме обслуживания , рекомендует выполнить переход на классы java.time .

Чтобы узнать больше, см. Учебное пособие по Oracle . И поиск переполнения стека для многих примеров и объяснений. Спецификация JSR 310 .

Вы можете обмениваться java.time объектами напрямую с вашей базой данных. Используйте драйвер JDBC , совместимый с JDBC 4.2 или более поздней версии. Нет необходимости в строках, нет необходимости в java.sql.* классах.

Где получить классы java.time?

Проект ThreeTen-Extra расширяет java.time дополнительными классами. Этот проект является полигоном для возможных будущих дополнений к java.time. Вы можете найти здесь несколько полезных классов, таких как Interval, YearWeek, YearQuarter и more .

0 голосов
/ 27 апреля 2018
zcal.set(Calendar.DAY_OF_MONTH, 31);
zcal.set(Calendar.MONTH, 1);
zcal.set(Calendar.YEAR, 2000);

Определяет дату 31 февраля 2000 года, которая из-за мягких измерений приравнивается ко 2 марта 2000 года. Значения месяца индексируются нулем.

Настройка строго:

zcal.setLenient(false);

Мы получаем исключение:

Exception in thread "main" java.lang.IllegalArgumentException: MONTH: 1 -> 2
at java.util.GregorianCalendar.computeTime(GregorianCalendar.java:2829)
at java.util.Calendar.updateTime(Calendar.java:3393)
at java.util.Calendar.getTimeInMillis(Calendar.java:1782)
at java.util.Calendar.getTime(Calendar.java:1755)
at Employee.Tester.main(Tester.java:19)

Итак, вы можете видеть, что интерполяция была перенесена на март. Если вы хотите жестко закодировать даты для целей тестирования, я бы рекомендовал использовать строгую проверку, чтобы избежать этих случаев. Были и другие задокументированные проблемы, связанные с установкой значений, которые усложняют ситуацию.

...