Подтвердить что ты не робот

Java SE 8 TemporalAccessor.from проблемы при использовании с java.time.Instant object

java.time имеет класс Instant, который инкапсулирует позицию (или "момент" ) на временной шкале. Хотя я понимаю, что это значение секунд/наносекунд, которое напрямую не связано с часовыми поясами или смещениями времени, его toString возвращает дату и время, отформатированные как дата/время UTC, например, 2014-05-13T20: 05: 08.556Z, Кроме того, anInstant.atZone(zone) и anInstant.atOffset(offset) генерируют значение, которое согласуется с обработкой Instant как подразумеваемого смещения UTC time zone/'zero'.

Я бы ожидал поэтому:

  • ZoneOffset.from(anInstant), чтобы создать "нуль" ZoneOffset
  • OffsetDateTime.from(anInstant) для создания даты/времени с нулевым смещением
  • ZoneId.from(anInstant) (возможно) для создания UTC ZoneId
  • ZonedDateTime.from(anInstant) (возможно) для создания ZonedDateTime с UTC ZoneId

Документация для ZonedDateTime.from, как я ее прочитал, подтверждает это.

Фактически ZoneOffset.from(anInstant) терпит неудачу с DateTimeException, и я полагаю, что по этой причине OffsetDateTime.from(anInstant) также терпит неудачу, как и другие два.

Является ли это ожидаемым поведением?

4b9b3361

Ответ 1

Короткий ответ:

Разработчики JSR-310 не хотят, чтобы люди делали преобразования между машинным временем и человеческим временем через static from() - методы в типах, таких как ZoneId, ZoneOffset, OffsetDateTime, ZonedDateTime и т.д. Это явно указано, если вы внимательно изучите javadoc. Вместо этого используйте:

OffsetDateTime#toInstant():Instant
ZonedDateTime#toInstant():Instant
Instant#atOffset(ZoneOffset):OffsetDateTime
Instant#atZone(ZoneId):ZonedDateTime

Проблема со статическими методами from() заключается в том, что в противном случае люди могут делать преобразования между Instant и, например, a LocalDateTime, не задумываясь о часовом поясе.

Длинный ответ:

Следует ли считать Instant как счетчик или полевой кортеж, ответ команды JSR-310 был строгим разделением между так называемым машинным временем и человеческим временем. На самом деле они намерены иметь строгое разделение - см. Их рекомендации. Поэтому, наконец, они хотят, чтобы Instant интерпретировался только как счетчик машинного времени. Поэтому они намеренно имеют дизайн, в котором вы не можете задавать Instant для полей, таких как год, час и т.д.

Но действительно, команда JSR-310 совсем несовместима. Они реализовали метод Instant.toString() как вид полевого кортежа, включая год,..., час,... и символ смещения Z (для часовой пояс UTC) (сноска: вне JSR-310 это довольно часто встречается полевой взгляд на такие машинные времена - см., например, в Википедии или на других сайтах о TAI и UTC). Как только спецификация привела С. Коулбурна в комментарии к threeten-github-issue:

"Если бы мы были очень жесткой линией, toString моментального времени просто было бы числом секунд с 1970-01-01Z. Мы решили не делать этого и выводить более дружественную toString, чтобы помочь разработчикам, но она не делает 'измените основной факт, что Instant - это всего лишь количество секунд и не может быть преобразовано в год/месяц/день без какой-либо временной зоны.

Людям может понравиться это дизайнерское решение или нет (например, я), но в результате вы не можете задавать Instant в течение года,..., часа,... и смещения. См. Также документацию поддерживаемых полей:

NANO_OF_SECOND 
MICRO_OF_SECOND 
MILLI_OF_SECOND 
INSTANT_SECONDS 

Здесь интересно то, чего не хватает, прежде всего отсутствует связанное с зоной поле. В качестве причины мы часто слышим утверждение, что объекты, такие как Instant или java.util.Date, не имеют часовой пояс. По-моему, это слишком упрощенное представление. Хотя верно, что эти объекты не имеют внутреннего состояния временной зоны (и также нет необходимости иметь такое внутреннее значение), эти объекты ДОЛЖНЫ быть связаны с часовым поясом UTC, потому что это является основой для расчета каждого смещения времени и преобразования в локальные типы, Таким образом, правильный ответ будет следующим: Instant - счетчик машин, считающий секунды и наносекунды с эпохи UNIX в часовом поясе UTC (по спецификации). Последняя часть - отношение к зоне UTC - недостаточно четко определена командой JSR-310, но они не могут ее отрицать. Дизайнеры хотят отменить аспект часового пояса от Instant, потому что он выглядит связанным с человеком.. Однако они не могут полностью отменить его, потому что это фундаментальная часть любого вычисления внутреннего смещения. Итак, ваше наблюдение за

"Кроме того, Instant.atZone(zone) и Instant.atOffset(offset) создают значение, которое согласуется с обработкой Instant как имеющего подразумеваемое смещение UTC time-zone/'zero'.

является правильным.

Хотя может показаться очень интуитивным, что ZoneOffset.from(anInstant) может генерировать ZoneOffset.UTC, он выдает исключение, потому что его метод from() ищет несуществующий OFFSET_SECONDS поле. Дизайнеры JSR-310 решили сделать это в спецификации по той же причине, а именно заставить людей думать, что Instant официально не имеет никакого отношения к часовому поясу UTC, то есть "не имеет часовой пояс" (но внутри они должны принять это основной факт во всех внутренних вычислениях!).

По той же причине, OffsetDateTime.from(anInstant) и ZoneId.from(anInstant) тоже не работают.

О ZonedDateTime.from(anInstant) we читать:

"Преобразование сначала получит ZoneId из временного объекта, если необходимо, возвращается к ZoneOffset, а затем попытается получить Instant, возвращаясь к LocalDateTime, если это необходимо. Результатом будет либо комбинация ZoneId или ZoneOffset с Instant или LocalDateTime."

Таким образом, это преобразование снова не удастся из-за тех же причин, потому что ни ZoneId, ни ZoneOffset не могут быть получены из Instant. Сообщение об ошибке читается как:

"Невозможно получить ZoneId от TemporalAccessor: 1970-01-01T00: 00: 00Z типа java.time.Instant"

Наконец, мы видим, что все статические из() - методов страдают от невозможности сделать преобразование между человеческим временем и машинным временем, даже если это выглядит интуитивно понятным. В некоторых случаях преобразование между let say LocalDate и Instant является сомнительным. Это поведение указано, но я предсказываю, что ваш вопрос не является последним вопросом такого рода, и многие пользователи будут по-прежнему путаться.

Реальная проблема дизайна на мой взгляд такова:

a) Не должно быть резкого разделения между человеческим временем и машинным временем. Временные объекты, такие как Instant, должны лучше вести себя как и то, и другое. Аналогия в квантовой механике: вы можете рассматривать электрон как частицу, так и волну.

b) Все статические методы() - слишком публичные. По моему мнению, я слишком легко доступен и лучше должен быть удален из общедоступного API или использовать более конкретные аргументы, чем TemporalAccessor. Слабостью этих методов является то, что люди могут забыть думать о связанных часовых поясах в таких преобразованиях, потому что они запускают запрос с локальным типом. Рассмотрим, например: LocalDate.from(anInstant) (в котором временной интервал???). Однако, если вы прямо задаете Instant для своей даты, например instant.getDate(), я лично считаю дату в UTC-часовом поясе действительным ответом, потому что здесь запрос начинается с перспективы UTC.

c) В заключение: я абсолютно разделяю с командой JSR-310 хорошую идею, чтобы избежать переходов между локальными типами и глобальными типами, такими как Instant без указания часового пояса. Я просто отличаюсь, когда речь заходит об API-дизайне, чтобы пользователи не делали такое преобразование без учета часовых поясов. Мой предпочтительный способ состоял бы в том, чтобы ограничить методы from(), а не говорить, что глобальные типы не должны иметь никакого отношения к форматам человеческого времени, таким как календарное или настенное время или смещение по часовой стрелке UTC.

В любом случае, этот (непоследовательный) дизайн разделения между машинным временем и человеческим временем теперь установлен в камне из-за сохранения обратной совместимости, и каждый, кто хочет использовать новый API java.time, должен жить с ним.

Извините за длинный ответ, но довольно сложно объяснить выбранный дизайн JSR-310.

Ответ 2

Класс Instant не имеет часового пояса. Он печатается как в зоне UTC, потому что он должен быть напечатан в некоторой зоне (вы не хотите, чтобы его печатали в тиках, не так ли?), Но это не часовой пояс - как показано в следующем примере:

Instant instant=Instant.now();
System.out.println(instant);//prints 2014-05-14T06:18:48.649Z
System.out.println(instant.atZone(ZoneId.of("UTC")));//prints 2014-05-14T06:18:48.649Z[UTC]

Подписи ZoneOffset.from и OffsetDateTime.from принимают любой временный объект, но для некоторых типов они терпят неудачу. java.time, похоже, не имеет интерфейса для временных, которые имеют часовой пояс или смещение. Такой интерфейс мог бы объявить getOffset и getZone. Поскольку у нас нет такого интерфейса, эти методы объявляются отдельно в нескольких местах.

Если у вас есть ZonedDateTime, вы можете назвать его getOffset. Если у вас есть OffsetDateTime, вы также можете назвать его смещением, но это два разных метода getOffset, поскольку эти два класса не получают этот метод из общего интерфейса. Это означает, что если у вас есть объект Temporal, который может быть либо вам нужно будет проверить, если он instanceof для обоих, чтобы получить смещение.

OffsetDateTime.from упрощает процесс, делая это для вас, но поскольку он также не может полагаться на общий интерфейс, он должен принимать любой объект Temporal и вызывать исключение для тех, у кого нет смещения.

Ответ 3

Просто пример преобразования w.r.t, я считаю, что некоторые люди получат меньше исключения

(java.time.DateTimeException: невозможно получить LocalDateTime из TemporalAccessor: 2014-10-24T18: 22: 09.800Z типа java.time.Instant)

если они попытаются -

LocalDateTime localDateTime = LocalDateTime.from(new Date().toInstant());

чтобы устранить проблему, пройдите в зону -

LocalDateTime localDateTime = LocalDateTime.from(new Date().toInstant().atZone(ZoneId.of("UTC")));