tl; dr
Вы, кажется, слишком много работаете.
Вы используете неправильные классы.Вместо этого используйте современные java.time классы.
myPreparedStatement.setObject(
… ,
Instant.ofEpochMilli( 1_528_371_000_000L )
)
В вашей базе данных используется неверный тип данных .Тип TIMEZONE WITHOUT TIME ZONE
нельзя использовать для сохранения определенного момента.
Избегать установки часового пояса по умолчанию
TimeZone.setDefault
Этот вызов немедленно влияет на весь код во всех потоках всех приложений в JVM!Так что делайте этот призыв только в самых отчаянных обстоятельствах.
Вместо этого передайте желаемый / ожидаемый часовой пояс в качестве необязательного аргумента ZoneId
многим из java.time методов.
часовой пояс по умолчанию вашей операционной системы и JVM должны быть не связаны с вашим кодом.Укажите явно желаемый / ожидаемый часовой пояс в виде объекта ZoneId
(или ZoneOffset
).
Избегайте устаревших классов даты и времени.
Старые классы даты и времени, связанные с ранними версиями Java, ужасны.Это включает в себя класс GregorianCalendar
, видимый в вашем коде.Вместо этого используйте их замену, современные java.time классы.
В частности, GregorianCalendar
заменяется на ZonedDateTime
.
Instant
Класс Instant
в java.time является основным строительным блоком.Класс Instant
представляет момент на временной шкале в UTC с разрешением наносекунд (до девяти (9) цифр десятичной дроби).
Instant instant = Instant.now() ; // Capture the current moment in UTC.
По-видимому, у вас есть счет в миллисекундах с начала отсчета эпохи из первого момента 1970 UTC , 1970-01-01T00: 00: 00Z.Просто проанализируйте этот номер как объект Instant
, вызвав ofEpochMilli
.
long input = 1_528_371_000_000L ;
Instant instant = Instant.ofEpochMilli( input ) ;
instant.toString (): 2018-06-07T11: 30: 00Z
Часовые пояса
Возможно, вы захотите представить Instant
пользователю в определенном часовом поясе.Примените ZoneId
, чтобы получить ZonedDateTime
.
Укажите правильное имя часового пояса в формате continent/region
, например America/Montreal
, Africa/Casablanca
или Pacific/Auckland
.Никогда не используйте 3-4-буквенные псевдозоны, такие как EST
или IST
, поскольку они не истинные часовые пояса, не стандартизированы и даже не уникальны (!).
ZoneId z = ZoneId.of( "Pacific/Auckland" ) ; // Or Europe/Paris, Africa/Tunis, etc.
База данных
Я не знаю, что вы делаете с XMLGregorianCalendar
.И я не могу обратиться к Hibernate, так как я не пользователь, хотя я знаю, что Hibernate поддерживает java.time типов .
Но если вы пытаетесь записать и извлечь моментс Postgres вы слишком много работаете.
Сохранение момента в UTC в столбец типа TIMESTAMP WTH TIME ZONE
( не WITHOUT
! - см. ниже).
String sql = "INSERT INTO tbl ( event ) VALUES ( ? ) ;" ; // Writing a moment into a column of type `TIMESTAMP WTH TIME ZONE`.
…
Instant instant = Instant.ofEpochMilli( 1_528_371_000_000L ) ; // Representing a moment in UTC.
myPreparedStatement.setObject( 1 , instant ) ; // As of JDBC 4.2 and later, we can directly exchange java.time objects with our database.
Извлечение.
Instant instant = myResultSet.getObject( … , Instant.class ) ;
Имейте в виду, что стандарт SQL едва затрагивает тему даты и времени.Различные базы данных ведут себя очень по-разному в своей обработке даты и времени.
Способ работы Postgres заключается в том, что TIMESTAMP WITH TIME ZONE
является чем-то неправильным.Часовой пояс , а не , хранящийся как часть метки времени.Любая предоставленная временная зона информации о смещении от UTC, представленная с помощью ввода, используется для настройки на UTC.Значение UTC записывается в базу данных.Зона / смещение затем отбрасывается / забывается.Если вам важно запомнить исходную зону / смещение входных данных, вы должны сохранить их отдельно в другом столбце.
Получая значение TIMESTAMP WITH TIME ZONE
из Postgres, вы всегда получаете значение в UTC.Это может быть неочевидно для вас, если ваш инструмент промежуточного программного обеспечения, соединяющий вас (ваше приложение) с базой данных, вводит собственное мнение относительно желаемого часового пояса для презентации.
Избежать этой путаницы с часовыми поясами легко с Java: Передавать Instant
объекты и извлекать Instant
объекты. Instant
всегда находится в UTC.Поэтому вызов ResultSet::getObject( … , Instant.class)
всегда будет точно представлять значение TIMESTAMP WITH TIME ZONE
базы данных.
Один сложный момент - разрешение доли секунды.Классы java.time используют разрешение наносекунд, а Postgres - микросекунды.Таким образом, вы можете явно усечь Instant
в своем коде, чтобы подтвердить этот факт.Ваш драйвер JDBC будет урезан для вас, но мне не нравятся такие закулисные манипуляции с данными.
Instant instant = Instant.now().truncatedTo( ChronoUnit.MILLISECONDS ) ; // Lop off any nanoseconds to match Postgres storing microseconds.
Неправильный тип данных в вашем столбце
Тип startTime:отметка времени без часового пояса в postgres.
TIMESTAMP WITHOUT TIME ZONE
- это неверный тип данных для хранения момента.В этом типе отсутствует понятие часового пояса или смещения от UTC.Так что это не может представлять конкретный момент.Он представляет собой набор потенциальных моментов в диапазоне около 26-27 часов (диапазон всех часовых поясов).
Вместо этого вы должны использовать тип TIMESTAMP WITH TIME ZONE
для хранения определенного момента на временной шкале.
Поэтому, когда мы сохраняем данные, он снова преобразует их в UTC.
Нет, совсем нет.Как раз наоборот.Любая включенная информация о часовом поясе или смещении от UTC игнорируется.Дата и время дня принимаются как есть, без учета часового пояса, без учета UTC.
Стандартный SQL-тип TIMEZONE WITHOUT TIME ZONE
эквивалентен java.time типу LocalDateTime
.
Вы должны использовать эти типы без зоны только в следующих трех ситуациях:
- Зона или смещение неизвестно .Это плохо.Это неверные данные.Аналогично наличию цены / стоимости без знания валюты.Вы должны отвергать такие данные, а не хранить их.
- Намерение «везде» , как в каждом часовом поясе.Например, корпоративная политика, которая гласит: «Все наши фабрики будут работать на обед в 12:30» означает, что фабрика в Дели сломается за несколько часов до фабрики в Дюссельдорфе, которая ломается за несколько часов до фабрики в Детройте.
- Момент в будущем намечен, но мы боимся политиков переопределить часовой пояс .Правительства меняют правила своих часовых поясов с удивительной частотой и с удивительно небольшим предупреждением или даже без предупреждения .Поэтому, если вы хотите записаться на прием в 3 часа дня на определенную дату, и вы действительно имеете в виду 3 часа дня, независимо от какого-либо сумасшедшего решения, которое правительство может принять за это время, сохраните
LocalDateTime
.Чтобы распечатать отчет или отобразить календарь, динамически примените часовой пояс (ZoneId
), чтобы сгенерировать определенный момент (ZonedDateTime
или Instant
).Это нужно делать на лету, а не сохранять значение.
О 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?
ThreeTen-Extra Проект расширяет java.time дополнительными классами.Этот проект является полигоном для возможных будущих дополнений к java.time.Здесь вы можете найти несколько полезных классов, таких как Interval
, YearWeek
, YearQuarter
и more .