Ответ 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.