Timestamp.from не обращая внимания на часовой пояс от Instant - PullRequest
0 голосов
/ 26 апреля 2018

Когда я пытаюсь преобразовать ZonedDateTime в Timestamp, все в порядке, пока я не вызову Timestamp.from() в следующем коде:

ZonedDateTime currentTimeUTC = ZonedDateTime.now(ZoneOffset.UTC);
currentTimeUTC = currentTimeUTC.minusSeconds(currentTimeUTC.getSecond());
currentTimeUTC = currentTimeUTC.minusNanos(currentTimeUTC.getNano());
return Timestamp.from(currentTimeUTC.toInstant());

ZonedDateTime.now(ZoneOffset.UTC); -> 2018-04-26T12:31Z
currentTimeUTC.toInstant() -> 2018-04-26T12:31:00Z
Timestamp.from(currentTimeUTC.toInstant()) -> 2018-04-26 14:31:00.0 
// (with Timezone of Europe/Berlin, which is currently +2)

Почему Timestamp.from() не учитывает часовой пояс, установленный в данный момент?

Ответы [ 3 ]

0 голосов
/ 26 апреля 2018

Класс Instant не имеет часового пояса, он просто имеет значения секунд и наносекунд со времен unix. Timestamp также представляет это (отсчет от эпохи).

почему отладчик отображает это с Z позади него?

Проблема в toString методах:

Instant.toString() преобразует значения секунд и наносекунд в соответствующую дату / время в UTC - отсюда и «Z» в конце - и я считаю, что это сделано для удобства (чтобы API был более «дружественным к разработчикам») «).

Javadoc для toString говорит:

Строковое представление этого момента с использованием представления ISO-8601.
Используемый формат такой же как DateTimeFormatter.ISO_INSTANT.

И если мы посмотрим на DateTimeFormatter.ISO_INSTANT Javadoc :

Модуль форматирования мгновенных сообщений ISO, который форматирует или анализирует момент в формате UTC, например, «2011-12-03T10: 15: 30Z»

Поскольку отладчики обычно используют метод toString для отображения значений переменных, это объясняет, почему вы видите Instant с "Z" в конце вместо значений секунд / наносекунд.

С другой стороны, Timestamp.toString использует часовой пояс JVM по умолчанию для преобразования значений секунд / нанос в строку даты / времени.

Но значения Instant и Timestamp одинаковы. Вы можете проверить это, вызвав методы Instant.toEpochMilli и Timestamp.getTime, и те, и другие вернут одно и то же значение.


Примечание: вместо вызова minusSeconds и minusNanos, вы можете использовать метод truncatedTo:

ZonedDateTime currentTimeUTC = ZonedDateTime.now(ZoneOffset.UTC);
currentTimeUTC = currentTimeUTC.truncatedTo(ChronoUnit.MINUTES);

Это установит все поля меньше ChronoUnit.MINUTES (в данном случае, секунд и наносекунд) в ноль.

Вы также можете использовать withSecond(0) и withNano(0), но в этом случае, я думаю, truncatedTo лучше и более прямо к сути.


Примечание 2: создатель java.time API также создал бэкпорт для Java 6 и 7, а в github проекта вы можете увидеть комментарий о поведении Instant.toString. Соответствующая часть к этому вопросу:

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

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

0 голосов
/ 27 апреля 2018

ТЛ; др

  • Вас смущает неудачное поведение Timestamp::toString при применении текущего часового пояса JVM по умолчанию к внутреннему значению UTC объектов.
  • ➡ Использовать Instant, никогда Timestamp.
  • Строка, такая как 2018-04-26T12:31Z, имеет стандартный ISO 8601 формат, где Z - это сокращение от Zulu и означает UTC .

Весь ваш блок кода можно заменить на:

Instant.now()

… таких как:

myPreparedStatement.setObject( … , Instant.now() ) ;

Подробнее

Ответ от wowxts правильный. Instant всегда в UTC, как и Timestamp, но Timestamp::toString применяет часовой пояс. Такое поведение является одним из многих неудачных вариантов дизайна в этих проблемных классах наследства.

Я добавлю несколько других мыслей.

Используйте Instant для UTC

ZonedDateTime currentTimeUTC = ZonedDateTime.now (ZoneOffset.UTC);

Хотя это технически правильно, эта строка семантически неверна. Если вы хотите представить момент в UTC, используйте Instant class. Класс Instant представляет момент на временной шкале в UTC с разрешением наносекунд (до девяти (9) цифр десятичной дроби).

Instant instant = Instant.now() ;  // Capture the current moment in UTC.

Избегайте наследства Timestamp класс

* * Timestamp.from тысяча шестьдесят один (currentTimeUTC.toInstant ()); * * тысяча шестьдесят-две

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

Timestamp.from( instant ); // Convert from modern *java.time* class to troublesome legacy date-time class using new method added to the old class.

Ничего не потеряно, если между Instant и Timestamp, поскольку оба представляют момент в UTC с разрешением наносекунд. Однако ...

Нет необходимости использовать java.sql.Timestamp! Этот класс является частью проблемных старых классов даты и времени, которые теперь унаследованы. Они были полностью вытеснены java.time классами, определенными JSR 310. Timestamp заменяется на Instant.

JDBC 4.2

Начиная с JDBC 4.2 и более поздних версий, вы можете напрямую обмениваться java.time объектами с вашей базой данных.

Insert / Update.

myPreparedStatement.setObject( … , instant ) ;

индексирование.

Instant instant = myResultSet.getObject( … , Instant.class ) ;

О java.time

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

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

Чтобы узнать больше, см. Oracle Tutorial . И поиск переполнения стека для многих примеров и объяснений. Спецификация JSR 310 .

Вы можете обмениваться java.time объектами напрямую с вашей базой данных. Используйте драйвер JDBC , совместимый с JDBC 4.2 или более поздней версии. Нет необходимости в строках, нет необходимости в java.sql.* классах.

Где получить классы java.time?

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

0 голосов
/ 26 апреля 2018

Instant не содержит информацию о часовом поясе. Он содержит только секунды и нанос. Когда вы конвертируете ZonedDateTime в мгновение, информация теряется. При преобразовании в Timestamp, Timestamp будет содержать часовой пояс по умолчанию, который, в вашем случае, Europe / Berlin.

...