Oracle / JDBC: проблема с правильным часовым поясом строк - PullRequest
0 голосов
/ 25 марта 2019

У меня есть таблица TAB с меткой времени (DT) и значением (VAL) на строку.

 DT           NOT NULL   TIMESTAMP(0) WITH LOCAL TIME ZONE
 VAL          NOT NULL   NUMBER

Я пытаюсь прочитать и получить метки времени в формате UTC.

SELECT SYS_EXTRACT_UTC (dt) AS dtm, val FROM mytab;

Результат в Sqlplus выглядит нормально для меня.

DTM                 VAL
---------------------------
30.03.19 23:00:00   124
31.03.19 00:00:00   125
31.03.19 01:00:00   126
31.03.19 02:00:00   127
31.03.19 03:00:00   128
31.03.19 04:00:00   129
31.03.19 05:00:00   130

Теперь я делаю то же самое в Java / JDBC.

java.sql.Timestamp ts = rs.getTimestamp ("dtm");
java.util.Date dt = rs.getDate ("dtm"); 
java.util.Date tm = rs.getTime ("dtm");
ZonedDateTime zdt = ts.toInstant ().atZone (ZoneOffset.UTC);

System.out.println("ts="+ts.toString() + "     val="+rs.getDouble("wert") +
     "   dt=" + dt.toString() + "    tm=" + tm.toString() +"      zdt="+zdt);

И результат не тот, который я ожидал в отношении часовых поясов.

ts=2019-03-30 23:00:00.0     val=124.0   dt=2019-03-30    tm=23:00:00      zdt=2019-03-30T22:00Z
ts=2019-03-31 00:00:00.0     val=125.0   dt=2019-03-31    tm=00:00:00      zdt=2019-03-30T23:00Z
ts=2019-03-31 01:00:00.0     val=126.0   dt=2019-03-31    tm=01:00:00      zdt=2019-03-31T00:00Z
ts=2019-03-31 03:00:00.0     val=127.0   dt=2019-03-31    tm=02:00:00      zdt=2019-03-31T01:00Z
ts=2019-03-31 03:00:00.0     val=128.0   dt=2019-03-31    tm=03:00:00      zdt=2019-03-31T01:00Z
ts=2019-03-31 04:00:00.0     val=129.0   dt=2019-03-31    tm=04:00:00      zdt=2019-03-31T02:00Z
ts=2019-03-31 05:00:00.0     val=130.0   dt=2019-03-31    tm=05:00:00      zdt=2019-03-31T03:00Z

Почему у ts есть двойной 3?Я ожидаю, что это будет в UTC.Единственное правильное время в тм.

Почему это?В чем здесь проблема?

JRE 1.8 Oracle 12c Linux с локальным часовым поясом CET / CEST

Ответы [ 2 ]

2 голосов
/ 26 марта 2019

Часовой пояс CET / CEST переключается на летнее время 2019-03-31 в 02:00:00, поэтому он пропускается с 02:00:00 до 03:00: 00.

getDate возвращает java.sql.Date, то есть java.util.Date в 2019-03-31 в полночь, поэтому нет летнего времени.

getTime возвращает java.sql.Time, то есть java.util.Date в 1970-01-01 в данный момент времени, поэтому DST.

getTimestamp возвращает java.sql.Timestamp, то есть java.util.Date на заданную дату и время, поэтому применяется DST, изменяющийся с 02:00 на 03:00. Однако значения из базы данных не имеют часового пояса, поэтому оставшиеся значения не сдвигаются. Это недостаток в обработке TIMESTAMP WITH LOCAL TIME ZONE.

Преобразование java.sql.Timestamp в java.time.Instant откорректирует по летнему времени, так что покалечит время.

Обратите внимание, что TIMESTAMP WITH LOCAL TIME ZONE не хранит часовой пояс, он просто говорит клиентскому API преобразовать часовой пояс сеанса от вашего имени. Фактическое значение в базе данных зависит от часового пояса базы данных, а не от часового пояса клиента или часового пояса сеанса.

1 голос
/ 25 марта 2019

Из Java Doc: https://docs.oracle.com/javase/8/docs/api/java/sql/Timestamp.html Timestamp, Тонкая оболочка вокруг java.util.Date, которая позволяет API JDBC идентифицировать его как значение SQL TIMESTAMP.Он добавляет возможность удерживать значение дробных секунд SQL TIMESTAMP, позволяя указывать дробные секунды с точностью до наносекунд.Временная метка также обеспечивает операции форматирования и синтаксического анализа для поддержки синтаксиса перехода JDBC для значений временной метки.

Точность объекта временной метки рассчитывается следующим образом:

  • 19, то естьколичество символов в гггг-мм-дд чч: мм: сс
  • 20 + с, которое представляет собой количество символов в гггг-мм-дд чч: мм: сс. [fff ...] иs представляет масштаб данной метки времени, ее точность в долях секунды.

Существует много способов форматирования метки времени, и один из таких примеров может быть следующим:

SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
Timestamp ts = Timestamp.valueOf("2019-03-30 23:00:00.0");
System.out.println(sdf.format(ts));
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...