Преобразовать 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);