Преобразуйте входную дату в формате ГГГГ-ММ-ДД'ТЧЧ: ММ: СС: ССС + ЧЧММ в метку java. sql .Time, указав часовой пояс из ввода. - PullRequest
0 голосов
/ 30 апреля 2020

У нас есть требование, при котором строка передается из пользовательского интерфейса в ГГГГ-ММ-ДД'ТХЧ: ММ: СС: ССС + ЧЧММ вместе с часовым поясом (например, 2020-01-20 'T'05: 40: 334-0500) который необходимо разобрать в sql метку времени для вставки в БД.

Все попытки игнорируют метку времени, отправленную с входа, и устанавливают локальную метку времени при установке в DB. Посоветуйте, пожалуйста, если есть решение?

Ответы [ 2 ]

1 голос
/ 30 апреля 2020

java .time и JDB C 4.2 или более поздней версии

Я рекомендую использовать java .time, современный Java API даты и времени, для работы с датой и временем. Класс java.sql.Timestamp плохо спроектирован, настоящий взлом на фоне уже плохо спроектированного класса java.util.Date. К счастью, оба они также давно устарели. Возможно, вы подумали, что вам нужна Timestamp для передачи значения даты и времени в вашу базу данных SQL. Начиная с JDB C 4.2 это уже не так.

Сначала проанализируем вашу строку:

    DateTimeFormatter uiFormatter = new DateTimeFormatterBuilder()
            .append(DateTimeFormatter.ISO_LOCAL_DATE_TIME)
            .appendPattern("XX")
            .toFormatter();

    String stringFromUi = "2020-01-20T05:40:34-0500";

    OffsetDateTime dateTime = OffsetDateTime.parse(stringFromUi, uiFormatter);

    System.out.println(dateTime);

Выведите это далеко:

2020-01 -20T05: 40: 34-05: 00

Я исправил вашу строку. Он настолько близок к формату ISO 8601, что я был уверен, что он должен соответствовать этому стандарту. Если вы запланировали миллисекунды, нет проблем, код будет работать с долями в секундах, например 2020-01-20T05:40:30.334-0500. Если бы вы могли убедить своих пользователей UI отправлять строку с двоеточием в смещении, например, 2020-01-20T05:40:34-05:00, вам не понадобится форматер, но вы можете использовать одно-аргумент OffsetDateTime.parse(String).

Далее, я предполагал, что вы запросили Timestamp для взаимодействия с вашей базой данных. И поскольку вы запросили Timestamp с часовым поясом со входа , я также предположил, что тип данных на стороне SQL равен timestamp with time zone. Что рекомендуется для отметок времени. В этом случае начиная с JDB C 4.2 вы напрямую передаете наш OffsetDateTime драйверу JDB C. Для упрощенного примера:

    PreparedStatement ps = yourDatabaseConnection.prepareStatement(
            "insert into your_table(your_timestamp_with_time_zone_column) values (?);");
    ps.setObject(1, dateTime);
    int rowsInserted = ps.executeUpdate();

Если вам нужен Timestamp для какого-то устаревшего API, который вы не можете себе позволить изменить сейчас, сначала знайте, что java.sql.Timestamp не может иметь часовой пояс или смещение от UT C. Хороший способ получить Timestamp - это конвертировать из Instant, другого современного класса:

    Instant instant = dateTime.toInstant();
    Timestamp ts = Timestamp.from(instant);
    System.out.println(ts);

В моем часовом поясе, Европа / Копенгаген, получается:

2020-01-20 11: 40: 34.0

Это правильно, хотя может показаться, что это 6 часов неправильно. Европа / Копенгаген был в UT C со смещением +01: 00 в январе. Когда мы печатаем Timestamp, мы неявно вызываем его метод toString. Timestamp.toString() использует настройку часового пояса JVM для визуализации возвращаемой строки. Таким образом, ваша строка даты и времени была сначала преобразована из UT C offset -05: 00 в какой-то внутренний формат, который нам не нужен, а затем в смещение +01:00, разница в 6 часов от начального точка.

Ссылки

0 голосов
/ 30 апреля 2020

@ Ajay Dsouza Вы пытались использовать Java 8 ZonedDateTime вместо стандартного Date для своего варианта использования и явно указали часовой пояс в выражении. Например:

ZonedDateTime receivedTimestamp = somevalue;
Timestamp ts = new Timestamp(receivedTimestamp.toInstant().toEpochMilli());
ps.setTimestamp(
   1, 
   ts, 
   Calendar.getInstance(TimeZone.getTimeZone(receivedTimestamp.getZone()))
); 

На самом деле проблема, с которой вы сталкиваетесь при преобразовании часового пояса из входной строки в java.sql.Timezone, на самом деле довольно хорошо известна. Это происходит потому, что по умолчанию JDB C использует локальный часовой пояс JVM во время преобразования, если вы не установите свойство (в Spring-Boot или настроить Hibernate) явно, чтобы изменить это, или вы можете использовать перегруженный подготовленный метод инструкции, как я предоставил выше, чтобы явно установить часовой пояс в инструкции.

PreparedStatement setTimestamp Javado c утверждает, что:

При использовании объекта Calendar драйвер может рассчитать временную метку с учетом пользовательского часового пояса. Если объект Calendar не указан, драйвер использует часовой пояс по умолчанию, то есть виртуальную машину, на которой запущено приложение.

В Spring-boot вы можете установить следующее свойство, чтобы установить часовой пояс глобально (это это не то, что вы хотите, но):

spring.jpa.properties.hibernate.jdbc.time_zone=UTC
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...