Рекомендуемое использование для Joda-Time DateMidnight

javdoc для LocalDate#toDateMidnight читается следующим образом:

Начиная с версии 1.5, рекомендуется избегать DateMidnight и использовать toDateTimeAtStartOfDay() вместо этого из-за подробного исключения ниже.

Этот метод генерирует исключение, если переключатели часовых поясов по умолчанию на летнее время в полночь, и это LocalDate представляет эта дата переключения. Проблема в том, что нет такого времени, как полночь в требуемую дату, и как таковое исключение.

Тот факт, что полночь не существует в определенных часовых поясах, кажется достаточной для того, чтобы избежать использования DateMidnight целиком (при условии, что ваш код не использует фиксированный часовой пояс, который, как известно, не имеет такой ситуации летнего времени, и ему никогда не понадобится использовать различные часовые пояса в будущем).

Однако DateMidnight не устарел, и в javadoc для класса DateMidnight нет аналогичной рекомендации или предупреждения. Более того, конструктор DateMidnight счастливо принимает мгновенный и часовой пояс таким образом, что полночь не существует в данный день, а не бросает IllegalArgumentException как LocalDate#toDateMidnight. Результирующий DateMidnight ведет себя как a DateTime со временем в начале дня.

Когда полночь не существует в данный день, почему LocalDate#toDateMidnight выдает исключение, а конструктор DateMidnight не работает? Каков рекомендуемый прецедент для DateMidnight, если таковой имеется?

Ответ 1

Нет веских оснований для использования DateMidnight. LocalDate - лучший вариант. Это потому, что полночь не возникает раз в год в определенных часовых поясах, полностью испортив удобство использования класса и создавая ошибки в приложениях.

Конструктор был исправлен, чтобы избежать наихудшей проблемы, однако просмотр объекта DateMidnight с внутренним миллисекундным значением, указывающим в 01:00, не очень велик.

Ответ 3

Или лучше использовать метод LocalDate toDateTimeAtStartOfDay напрямую, чтобы обойти создание объекта DateTime (относительно ответить выше).

new LocalDate().toDateTimeAtStartOfDay( myDateTimeZone )

Ответ 4

TL;DR

Используйте java.time классы, в частности LocalDate::atStartOfDay вместо скользкой идеи о "полночь".

ZoneId z = ZoneId.of( "America/Montreal" );  // A time zone.
ZonedDateTime todayStart = LocalDate.now( z ).atStartOfDay( z );  // Produces a LocalDate (a whole day without time zone), then transforms into a `ZonedDateTime` (a moment on the timeline)

java.time

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

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

A часовой пояс имеет решающее значение для определения даты. В любой данный момент дата изменяется по всему миру по зонам. Например, через несколько минут после полуночи в Париж Франция - это новый день, пока еще "вчера" в Монреаль Квебек.

ZoneId z = ZoneId.of( "America/Montreal" );
LocalDate today = LocalDate.now( z );

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

Пусть java.time определяет время настенных часов этого первого момента дня. Не предполагайте, что время будет 00:00:00, поскольку аномалии, такие как переход на летнее время (DST), могут означать, что первым моментом является время, например 01:00:00. Такие корректировки DST в настоящее время используются во временных зонах нескольких стран.

Итак, чтобы получить мгновение, фактическая точка на временной шкале, для начала дня вызовите LocalDate::atStartOfDay. Обратите внимание, что это более короткая версия имени метода, чем используется в методе Joda-Times withTimeAtStartOfDay. Укажите желаемый/ожидаемый часовой пояс в ZoneId, чтобы создать объект ZonedDateTime.

ZoneId z = ZoneId.of( "America/Montreal" );
ZonedDateTime zdt = today.atStartOfDay( z );

Half-Open

Итак, как представить промежуток времени? Если я хочу точно определить начало и конец этого единственного дня, как мне это сделать, а также следовать этому совету? Решение, обычно используемое в работе с датой, - это подход Half-Open. В этом подходе начало диапазона включается, в то время как окончание является исключительным. Поэтому "сегодня" означает начать с первого момента дня и проделать весь путь до первого, но не включая, первого момента следующего дня.

ZonedDateTime zdtStartToday = LocalDate.now( z ).atStartOfDay( z );
ZonedDateTime zdtStartTomorrow = zdtStartToday.plusDays( 1 );

Кстати, проект ThreeTen-Extra имеет удобный Interval для таких промежутков времени.

Interval todayInterval = Interval.of( 
        zdtStartToday.toInstant() ,
        zdtStartTomorrow.toInstant()
    )

О java.time

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

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

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

Большая часть функциональных возможностей java.time портирована на Java 6 и 7 в ThreeTen-Backport и далее адаптирована к Android в ThreeTenABP (см. Как использовать...).

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

Ответ 5

Посмотрите на исключение, которое я имел в своем коде

Illegal instant due to time zone offset transition (daylight savings time 'gap'): 2015-03-27T00:00:00.000 (Asia/Amman)
    org.joda.time.IllegalInstantException: Illegal instant due to time zone offset transition (daylight savings time 'gap'): 2015-03-27T00:00:00.000 (Asia/Amman)

Теперь я решил это, используя

LocalDate currentDate=new LocalDate();
someMethodSetsTheDate(currentDate.toDateTimeAtStartOfDay().toDate());

Вместо

someMethodSetsTheDate(new DateMidnight(date.getYear(), date.getMonthOfYear(), date.getDayOfMonth()).toDate());

Теперь моя рекомендация - использовать .toDateTimeAtStartOfDay() чтобы избежать подобных исключений.

Пожалуйста, не стесняйтесь редактировать мой ответ Спасибо