java.time эквивалент Joda-Time `withTimeAtStartOfDay`? (получить первый момент дня)

В библиотеке Joda-Time _ 1_ предлагает метод withTimeAtStartOfDay, чтобы узнать первое мгновение дня. Вы можете подумать об этом моменте как о «полночь». Этот первый момент обычно - время 00:00:00.000, но не всегда.

Имеется ли пакет java.time в Java 8 и более поздних версиях есть эквивалентная функция?


person Basil Bourque    schedule 18.05.2015    source источник
comment
AFAIK не напрямую, но вы могли бы сделать что-то вроде LocalDateTime.now().withHour(0).withMinute(0).withSecond(0).withNano(0);, что немного затянуто, но должно достичь того же результата: P   -  person MadProgrammer    schedule 18.05.2015


Ответы (4)


В эквиваленте используется специальный метод atStartOfDay в классе LocalDate:

ZoneId zoneId = ZoneId.of("America/New_York");
ZonedDateTime zdt = LocalDate.now(zoneId).atStartOfDay(zoneId);

Также обратите внимание, что эквивалент Joda-Time _ 3_ не является _ 4_, но _ 5_. Здесь имеет значение параметр zoneId. Конкретный пример миграции - см. Также веб-сайт часовых поясов для получения дополнительной информации о переход на летнее время в Бразилии:

Джода-Тайм (старый способ)

DateTime dt = 
  new DateTime(2015, 10, 18, 12, 0, DateTimeZone.forID("America/Sao_Paulo"));
dt = dt.withTimeAtStartOfDay();
System.out.println(dt); // 2015-10-18T01:00:00.000-02:00

Обратите внимание, что этот код даже вызовет исключение для полуночи в первой строке с вызовом конструктор.

java.time (новый способ)

ZoneId zoneId = ZoneId.of("America/Sao_Paulo");
ZonedDateTime zdt =
  ZonedDateTime.of(2015, 10, 18, 12, 0, 0, 0, zoneId);
zdt = zdt.toLocalDate().atStartOfDay(zoneId);
System.out.println(zdt); // 2015-10-18T01:00-02:00[America/Sao_Paulo]

Второй оператор программы ведет себя иначе, чем Joda-Time, потому что он не генерирует исключение, а незаметно сдвигает местное время на размер рассматриваемого промежутка, здесь на один час. Это означает, что если бы вы выбрали полночь, результат был бы таким же (а именно 1:00). Если бы вы выбрали 00:30, то результат был бы 01:30. В приведенном выше примере в качестве входных данных выбран полдень.

Чтобы процитировать документ для ZonedDateTime.of (…):

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

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

100% -ная миграция всех деталей, таких как поведение исключений и применяемые стратегии перехода на летнее время, невозможна, потому что обе библиотеки слишком разные. Но это ваша рекомендация:

  • заменить DateTime на ZonedDateTime
  • рассмотрите возможность перехода на LocalDate для промежуточных вычислений (см. пример)
  • используйте явные ссылки на часовой пояс и замените DateTimeZone на ZoneId
person Meno Hochschild    schedule 18.05.2015
comment
Если вторая строка (ZonedDateTime.of) скользит вперед или назад, зачем нам третья строка, zdt.toLocalDate().atStartOfDay(zoneId)? - person Basil Bourque; 27.06.2015
comment
@BasilBourque Прежде всего позвольте мне поблагодарить вас за исправления английского языка. Насчет значения второй строки, ну без этой строки zdt будет как раз в полдень (исходное значение 12:00). Но речь идет о НАЧАЛЕ дня. Альтернативным и, возможно, более подходящим ответом на ваш вопрос будет: ZonedDateTime.of(2015, 10, 18, 0, 0, 0, 0, zoneId), который дает время на один час после полуночи (молча продвигаясь вперед на один час). Лично мне стиль, использующий метод atStartOfDay(...), более выразителен. - person Meno Hochschild; 27.06.2015
comment
@BasilBourque В обеих библиотеках отсутствует возможность не предлагать альтернативные стратегии перехода. Поведение Joda-Time просто для выдачи исключения для недействительного местного времени вряд ли удовлетворительно. И JSR-310 отказался от этой функции (когда-то был в Threeten!) и теперь ограничивается только стратегией PUSH_FORWARD. - person Meno Hochschild; 27.06.2015
comment
@BasilBourque Чтобы было понятнее. Мне вообще не нравятся неявные настройки по умолчанию и тихие настройки. Если API предлагает хоть какую-то возможность сделать корректировку с продвижением вперед более понятной (здесь atStartOfDay(ZoneId)), то я предпочитаю и защищаю это. В противном случае я сожалею, что JSR-310 по замыслу предлагает слишком много неявных значений по умолчанию. - person Meno Hochschild; 27.06.2015

Для этого можно использовать LocalDate:

// or a similar factory method to get the date you want
LocalDateTime startOfDay = LocalDate.now().atStartOfDay();

Объединяет эту дату со временем полуночи, чтобы создать LocalDateTime в начале этой даты. Это возвращает LocalDateTime, сформированный с этой даты в полночь, 00:00, в начале этой даты.

https://docs.oracle.com/javase/8/docs/api/java/time/LocalDate.html#atStartOfDay--

person Steve Chaloner    schedule 18.05.2015
comment
@MenoHochschild Почему ты так думаешь? Он даже включил ссылку на документацию JDK 8 API для метода - это не код Joda Time. - person Jesper; 18.05.2015
comment
@Jesper Пожалуйста, посмотрите пример кода еще раз. JSR-310 (Java-8) не использует общедоступные конструкторы, а использует статические фабричные методы. - person Meno Hochschild; 18.05.2015

Усечь

Хотя это старый вопрос, я только что открыл другой способ сделать это, используя truncated, который мне кажется более элегантным:

ZonedDateTime now = ZonedDateTime.now();
ZonedDateTime today = now.truncateTo(ChronoUnit.DAYS); // today, midnight

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

person Ben    schedule 19.02.2016

TemporalAdjuster

Каждый LocalTime также является _ 3_, поэтому if можно передать в with() для большинства временных типов и обновит поля времени для соответствия.

То есть:

@Test
public void date_time_at_start_of_day() throws Exception {
    assertThat(LocalDateTime.parse("2015-05-22T12:27:00")
                            .with(LocalTime.MIDNIGHT),
               equalTo(LocalDateTime.parse("2015-05-22T00:00:00")));
    assertThat(OffsetDateTime.parse("2015-05-22T12:27:00+01:00")
                             .with(LocalTime.MIDNIGHT),
               equalTo(OffsetDateTime.parse("2015-05-22T00:00:00+01:00")));
    assertThat(ZonedDateTime.parse("2015-05-22T12:27:00+01:00[Europe/London]")
                            .with(LocalTime.MIDNIGHT),
               equalTo(ZonedDateTime.parse("2015-05-22T00:00:00+01:00[Europe/London]")));
}
person araqnid    schedule 22.05.2015