GregorianCalendar не может добавить 97 дней? - PullRequest
0 голосов
/ 05 декабря 2018

Я создаю 3 объекта GregorianCalendar:

  1. 4 декабря 2018 года
  2. 4 декабря 2018 года + 96 дней
  3. 4 декабря 2018 года + 97 дней

Разница в днях между первым и вторым составляет 96 дней.Дневная разница между первым и третьим составляет ... 96 дней .Скажи, да?

Извиняюсь за код Scala, но вы, Java-руководители, должны быть в состоянии понять, что происходит:

def test(): Unit = {
    val start = new GregorianCalendar(2018, 11, 4)
    val laterA = new GregorianCalendar(2018, 11, 4)
    laterA.add(Calendar.DATE, 96)
    val laterB = new GregorianCalendar(2018, 11, 4)
    laterB.add(Calendar.DATE, 97)
    println(ChronoUnit.DAYS.between(start.toInstant, laterA.toInstant))
    println(ChronoUnit.DAYS.between(start.toInstant, laterB.toInstant))
  }

Выше приведено следующее:

96
96

В чем дело?

Ответы [ 4 ]

0 голосов
/ 06 декабря 2018

Как уже говорили другие, избегайте класса GregorianCalendar, если можете.У него есть некоторые проблемы с дизайном, и он уже давно устарел.

Однако ваша конкретная проблема была не в классе GregorianCalendar.Он добавляет 97 дней в ваш код, принимая во внимание летнее время (DST) и все остальное.Как правильно отметили другие, добавление 97 дней к 4 декабря этого года дает вам 11 марта, день, когда летнее время только начинается в тех часовых поясах Северной Америки, где используется летнее время (месяц 11 в качестве аргумента конструктора, означающего декабрь, еще одна запутанная вещьоколо GregorianCalendar).Поэтому последний добавленный день длился всего 23 часа.

Ваша настоящая проблема связана с комбинацией ChronoUnit.DAYS и Instant.Instant является моментом времени без часового пояса и не имеет понятия дней.Вместо этого вы получите тот же результат, что и при подсчете часов, делении на 24 и отбрасывании остатка.Это редко полезно.А поскольку последний добавленный день составил всего 23 часа, он не учитывается.Вместо этого посчитайте дни между ZonedDateTime или LocalDate экземплярами или каким-либо другим классом java.time, в названии которого есть «date».Пример 1 (код Java, можете ли вы перевести себя?):

    LocalDate start = LocalDate.of(2018, Month.DECEMBER, 4);
    LocalDate laterB = start.plusDays(97);
    System.out.println(ChronoUnit.DAYS.between(start, laterB));

Вывод:

97

Если вы не можете избежать получения GregorianCalendar из устаревшего API, который вы не можете позволить себе сейчас обновить, первое, что вы должны сделать, - это преобразовать его, используя toZonedDateTime, чтобы получить ZonedDateTime.Это даст вам всю необходимую информацию от GregorianCalendar, включая его часовой пояс и календарную дату.Другими словами, вы не должны использовать метод toInstant, как вы это делали в коде вопроса.Пример 2:

    GregorianCalendar start = new GregorianCalendar(2018, Calendar.DECEMBER, 4);
    GregorianCalendar laterB = new GregorianCalendar(2018, Calendar.DECEMBER, 4);
    laterB.add(Calendar.DATE, 97);
    System.out.println(ChronoUnit.DAYS.between(start.toZonedDateTime(),
                                               laterB.toZonedDateTime()));

Результат теперь тот, который вы ожидали:

97

Я запустил оба фрагмента с America / New_Yorkв качестве часового пояса по умолчанию.

0 голосов
/ 06 декабря 2018

jshell> System.out.println (LaterB.toInstant ());2019-03-11T04: 00: 00Z

jshell> System.out.println (LaterA.toInstant ());2019-03-10T05: 00: 00Z

Обратите внимание, что разница составляет менее 24 часов.Зачем?Вы перешли границу перехода на летнее время 10 марта.

0 голосов
/ 06 декабря 2018

Это из-за вашего часового пояса.Если вы сравните второе значение, выполнив

System.out.println(ChronoUnit.SECONDS.between(start.toInstant(), laterA.toInstant()));
System.out.println(ChronoUnit.SECONDS.between(start.toInstant(), laterB.toInstant()));

, вы увидите, что для Europe/London вы получите

8294400
8380800

, а для America/New_York это будет

8294400
8377200

разница в секундах составляет ровно 3600 секунду, что означает изменение летнего времени.

0 голосов
/ 06 декабря 2018

Вы должны быть в каком-либо регионе США (или в регионе, который начинает переход на летнее время одновременно с США).В 2019 году летнее время начинается в 2:00 утра, воскресенье, 10 марта 2019 года .

Добавление 96 дней с 4 декабря 2018 года дает 10 марта 2019 года. Добавление 97 дней с 4 декабря,2018 дает 11 марта 2019 года.

Форматирование и вывод laterA и laterB выходов:

2019-03-10 00:00:00
2019-03-11 00:00:00

Обратите внимание, что между этими двумя датами 23 часа из-за летнего времени (вСША).

Но метод between усекает дробные единицы.

Вычисление возвращает целое число, представляющее количество полных единиц между двумяtemporals.Например, количество часов в промежутке между 11:30 и 13:29 будет составлять только один час, поскольку одной минуте не хватает двух часов.

Таким образом, разница составляет 96 дней и 23 часа.(не 97 дней из-за летнего времени) возвращается как 96.

...