Преобразовать OffsetDateTime в UTC
У меня есть java.time.OffsetDateTime
, который я бы хотел преобразовать в java.sql.Timestamp
. Поскольку Timestamp
не хранит информацию о смещении, я собираюсь хранить все даты/время в базе данных в формате UTC.
Как мне преобразовать OffsetDateTime
в Timestamp
, который находится в UTC?
EDIT:
Я считаю, что это ответ, но, похоже, он довольно запутанный способ скрывать до UTC:
OffsetDateTime dateTime = OffsetDateTime.now();
Timestamp timestamp = Timestamp.valueOf(dateTime.atZoneSameInstant(ZoneId.of("Z")).toLocalDateTime());
Ответы
Ответ 1
Это будет способ сделать преобразование и обеспечить использование UTC. Я думаю, что это немного чище, чем предлагаемое решение с использованием секунд эпохи.
Timestamp test = Timestamp.valueOf(entityValue.atZoneSameInstant(ZoneOffset.UTC).toLocalDateTime());
Ответ 2
Другим решением будет:
Timestamp.valueOf(LocalDateTime.ofInstant(dateTime.toInstant(), ZoneOffset.UTC));
Он преобразует dateTime
в UTC, разбивает информацию о часовом поясе и затем преобразует результат в Timestamp
. Он все еще запутан, но ИМХО немного чище.
Просто используя toInstance()
или toEpochSeconds()
, отрегулируйте результат с предоставленным смещением.
Ниже показаны результаты теста из этого и других ответов:
OffsetDateTime dateTime =
OffsetDateTime.of(2015, 10, 23, 12, 44, 43, 0, ZoneOffset.UTC);
// OffsetDateTime.of(2015, 10, 23, 12, 44, 43, 0, ZoneOffset.ofHours(-5));
err.println("dateTime = "
+ dateTime
);
err.println("as LocalDateTime = "
+ dateTime.toLocalDateTime()
);
err.println("as timestamp (mine) = "
+ Timestamp.valueOf(LocalDateTime.ofInstant(dateTime.toInstant(), ZoneOffset.UTC))
);
err.println("@Cheetah (correct) = "
+ Timestamp.valueOf(dateTime.atZoneSameInstant(ZoneId.of("Z"))
.toLocalDateTime())
);
err.println("@Notso (wrong) = "
+ Timestamp.from(dateTime.toInstant())
);
err.println("@Glorfindel (wrong) = "
+ new Timestamp(1000 * dateTime.toEpochSecond())
);
который дает следующие результаты (мой часовой пояс - CET):
(with ZoneOffset.UTC)
dateTime = 2015-10-23T12:44:43Z
as LocalDateTime = 2015-10-23T12:44:43
as timestamp (mine) = 2015-10-23 12:44:43.0
@Cheetah (correct) = 2015-10-23 12:44:43.0
@Notso (wrong) = 2015-10-23 14:44:43.0
@Glorfindel (wrong) = 2015-10-23 14:44:43.0
(with ZoneOffset.ofHours(-5))
dateTime = 2015-10-23T12:44:43-05:00
as LocalDateTime = 2015-10-23T12:44:43
as timestamp (mine) = 2015-10-23 17:44:43.0
@Cheetah (correct) = 2015-10-23 17:44:43.0
@Notso (wrong) = 2015-10-23 19:44:43.0
@Glorfindel (wrong) = 2015-10-23 19:44:43.0
(Версия от Notso выше была до его редактирования от 17 февраля 2016 года)
Ответ 3
Используйте .toEpochSecond()
, чтобы получить количество секунд от контрольной даты (в UTC), умножить на 1000 и передать это конструктор Timestamp
(так как он ожидает миллисекунды).
new Timestamp(1000 * offsetDateTime.toEpochSecond());
Ответ 4
Я даю современный ответ.
java.time и JDBC 4.2
Вам следует избегать класса Timestamp
. Его плохо разработанный и очень запутанный, настоящий взлом на вершине и без того плохо спроектированного класса java.util.Date
. Тот факт, что другие ответы приводят к другим результатам, что подтверждается сравнениями в ответе rve, по моему мнению, очень хорошо иллюстрирует путаницу. Вы уже используете OffsetDateTime
из java.time, современного Java-API даты и времени, и при условии, что у вас есть JDBC 4.2-совместимый драйвер JDBC, вы можете и должны придерживаться классов из java.time.
Лучше всего хранить как timestamp with time zone
Хранение даты и времени в формате UTC в базе данных, как вы говорите, является хорошей и рекомендуемой практикой. Если можете, измените тип данных в базе данных на timestamp with time zone
. Хотя здесь не хранится часовой пояс (несмотря на название), он гарантирует, что база данных тоже "знает", что метки времени находятся в UTC, что уже предотвращает множество ошибок. Следующее преимущество заключается в том, что (при условии, что я правильно понял) вы можете сохранить свой OffsetDateTime
напрямую и позволить преобразованию в UTC происходить автоматически.
OffsetDateTime odt = OffsetDateTime.of(
2015, 6, 4, 19, 15, 43, 210987000, ZoneOffset.ofHours(1));
PreparedStatement stmt = yourDbConnection.prepareStatement(
"insert into your_table (your_timestamp_with_time_zone) values (?);");
stmt.setObject(1, odt);
stmt.executeUpdate();
Если вы хотите прояснить в своем коде Java, что время хранится в формате UTC, сначала выполните явное преобразование:
odt = odt.withOffsetSameInstant(ZoneOffset.UTC);
Если ваша база данных хранит timestamp
без часового пояса
Если тип данных в вашей базе данных является простым timestamp
(без часового пояса) (не рекомендуется), тип для использования на стороне Java - LocalDateTime
. Я бы сделал преобразование в UTC следующим образом:
LocalDateTime ldt = odt.withOffsetSameInstant(ZoneOffset.UTC).toLocalDateTime();
System.out.println("UTC datetime = " + ldt);
Вывод:
Время по Гринвичу = 2015-06-04T18: 15: 43.210987
Хранение в базе данных аналогично предыдущему:
PreparedStatement stmt = yourDbConnection.prepareStatement(
"insert into your_table (your_timestamp) values (?);");
stmt.setObject(1, ldt);