Oracle: местный часовой пояс, дифференцировать двойной час 2 - PullRequest
0 голосов
/ 19 декабря 2018

У меня есть Oracle-таблица (MYTAB) со столбцами для даты и времени (MYDT) и значением (MYVAL).

MYDT         NOT NULL TIMESTAMP(0) WITH LOCAL TIME ZONE
MYVAL        NOT NULL NUMBER

Давайте посмотрим переключение часового пояса CEST на CET,Каждое последнее воскресенье октября по местному времени используется двойной час 2 - первый час 2 - в CEST, второй - в CET.

У меня есть запись для каждого часа в моей таблице.Допустим, MYVAL для 1-го часа 2 равно 100, а для 2-го часа 2 - 200.

Я могу правильно указать значение в SELECT.

Получить 1-й час 2:

SQL> SELECT TO_TIMESTAMP_TZ(MYDT, 'DD.MM.YYYY HH24:MI TZH'), MYDT, MYVAL    
     FROM MYTAB 
     WHERE MYDT = TO_TIMESTAMP_TZ ('28.10.2018 00:00 00', 'DD.MM.YYYY HH24:MI TZH');

28.10.18 02:00:00 +00:00
28.10.18 02:00:00
111

Получите 2-й час 2:

SQL> SELECT  TO_TIMESTAMP_TZ(MYDT, 'DD.MM.YYYY HH24:MI TZH'), MYDT, MYVAL 
     FROM MYTAB 
     WHERE MYDT = TO_TIMESTAMP_TZ ('28.10.2018 01:00 00', 'DD.MM.YYYY HH24:MI TZH');

28.10.18 02:00:00 +00:00
28.10.18 02:00:00
222

Итак, я получил правильное значение относительно моей отметки времени.

Но в обоих случаях временной интервал одинаков - подумайте, потому что он хранится в местном времени.

Как я могу получить уникальную временную метку?Это должно быть возможно, потому что информация должна быть там, иначе SELECT не даст мне правильное значение относительно часового пояса в WHERE.

Спасибо!

Ответы [ 3 ]

0 голосов
/ 19 декабря 2018

Во-первых, «я могу правильно указать значение в SELECT» не совсем верно.Эти:

TO_TIMESTAMP_TZ(MYDT, 'DD.MM.YYYY HH24:MI TZH')
TO_TIMESTAMP_TZ ('28.10.2018 00:00 00', 'DD.MM.YYYY HH24:MI TZH');

не делают то, что вы думаете.В первом случае вы выполняете неявное преобразование значения mydt в строку, используя параметр NLS_TIMESTAMP_FORMAT вашего сеанса, который дает вам что-то вроде '28 .10.18 02:00:00 ';и затем в обоих из них вы конвертируете строку, используя предоставленную вами маску формата.Но смещение часового пояса обеспечивается значением секунд из строки.

В обоих ваших примерах это означает, что TZH установлен в ноль, что вы видите в выходных данных.Если у вас есть исходное значение, скажем, «28 .10.18 02:00:03», то оно станет «2018-10-28 02:00:00 +03: 00», что совсем не то, что вы хотели.И если есть какие-либо значения, где секунды выше 14, то произойдет сбой с «ORA-01874: часовой пояс должен быть в диапазоне от -12 до 14».

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

Если некоторые примеры данных и настройки сеанса совпадают с тем, что, я думаю, у вас есть:

create table mytab(
  mydt timestamp(0) with local time zone not null,
  myval number not null
);

insert into mytab (mydt, myval) values (timestamp '2018-10-28 01:59:59 CET CEST', 1);
insert into mytab (mydt, myval) values (timestamp '2018-10-28 02:00:00 CET CEST', 2);
insert into mytab (mydt, myval) values (timestamp '2018-10-28 02:00:00 CET CET', 3);
insert into mytab (mydt, myval) values (timestamp '2018-10-28 02:00:01 CET CET', 4);
insert into mytab (mydt, myval) values (timestamp '2018-10-28 02:00:02 CET CET', 5);
insert into mytab (mydt, myval) values (timestamp '2018-10-28 02:00:03 CET CET', 6);

alter session set time_zone = 'Europe/Berlin';
alter session set nls_timestamp_format = 'DD.MM.YYYY HH24:MI:SS';
alter session set nls_timestamp_tz_format = 'DD.MM.YYYY HH24:MI:SS TZH:TZM';

, тогда вы можете увидеть проблему более четко:

select mydt, to_timestamp_tz(mydt, 'DD.MM.YYYY HH24:MI TZH')
from mytab;

Error report -
ORA-01874: time zone hour must be between -12 and 14

select mydt, to_timestamp_tz(mydt, 'DD.MM.YYYY HH24:MI TZH')
from mytab
where myval > 1;

MYDT                TO_TIMESTAMP_TZ(MYDT,'DD.M
------------------- --------------------------
28.10.2018 02:00:00 28.10.2018 02:00:00 +00:00
28.10.2018 02:00:00 28.10.2018 02:00:00 +00:00
28.10.2018 02:00:01 28.10.2018 02:00:00 +01:00
28.10.2018 02:00:02 28.10.2018 02:00:00 +02:00
28.10.2018 02:00:03 28.10.2018 02:00:00 +03:00

Возможно, вы ожидали что-то более похожее на to_char(mydt, 'DD.MM.YYYY HH24:MI TZH'), но это выдает «ORA-01821: формат даты не распознан».Однако вместо этого вы можете увидеть флаг региона и перехода на летнее время:

select mydt, to_char(mydt, 'DD.MM.YYYY HH24:MI TZR TZD')
from mytab;

MYDT                TO_CHAR(MYDT,'DD.MM.YYYYHH24:MITZRTZD')                 
------------------- --------------------------------------------------------
28.10.2018 01:59:59 28.10.2018 01:59 EUROPE/BERLIN CEST                     
28.10.2018 02:00:00 28.10.2018 02:00 EUROPE/BERLIN CEST                     
28.10.2018 02:00:00 28.10.2018 02:00 EUROPE/BERLIN CET                      
28.10.2018 02:00:01 28.10.2018 02:00 EUROPE/BERLIN CET                      
28.10.2018 02:00:02 28.10.2018 02:00 EUROPE/BERLIN CET                      
28.10.2018 02:00:03 28.10.2018 02:00 EUROPE/BERLIN CET                      

Тем не менее, он все равно будет отображать строку с использованием часового пояса сеанса.

Аналогично ваш фильтр может выглядеть примерно так:

select myval,
  mydt,
  sys_extract_utc(mydt) as mydt_utc,
  to_char(mydt, 'DD.MM.YYYY HH24:MI TZR TZD') as mydt_full
from mytab
where mydt = to_timestamp_tz('28.10.2018 02:00:00 CET CEST', 'DD.MM.YYYY HH24:MI:SS TZR TZD');

     MYVAL MYDT                MYDT_UTC            MYDT_FULL                          
---------- ------------------- ------------------- -----------------------------------
         2 28.10.2018 02:00:00 28.10.2018 00:00:00 28.10.2018 02:00 EUROPE/BERLIN CEST

и

select myval,
  mydt,
  sys_extract_utc(mydt) as mydt_utc,
  to_char(mydt, 'DD.MM.YYYY HH24:MI TZR TZD') as mydt_full
from mytab
where mydt = to_timestamp_tz('28.10.2018 02:00:00 CET CET', 'DD.MM.YYYY HH24:MI:SS TZR TZD');

     MYVAL MYDT                MYDT_UTC            MYDT_FULL                          
---------- ------------------- ------------------- -----------------------------------
         3 28.10.2018 02:00:00 28.10.2018 01:00:00 28.10.2018 02:00 EUROPE/BERLIN CET 

Я включил вывод sys_extract_utc() в качестве альтернативы вашему cast().Вы также можете использовать mydt at time zone 'UTC' без приведения, что дает вам метку времени со значением часового пояса.

Опять же, изменение часового пояса сеанса изменит способ отображения значения mydt_full, но фильтр все равно будетработать, потому что правильный часовой пояс был явно указан там.И в зависимости от того, откуда поступают значения фильтра, вы можете использовать литерал отметки времени , как я делал в моих операторах вставки.

Узнайте больше о том, как отметка времени с местным временемтип данных зоны.

0 голосов
/ 19 декабря 2018

Значения меток времени не сохраняются в «местном времени», они хранятся в DBTIMEZONE.Однако это скорее внутренняя тема Oracle, и она не очень актуальна.

Вместо cast(MYDT at time zone 'UTC' as date) вы также можете использовать SYS_EXTRACT_UTC(MYDT)

Чтобы сделать его более понятным, попробуйте

SELECT 
    SYS_EXTRACT_UTC(MYDT), 
    TO_CHAR(MYDT, 'dd.mm.yyyy hh24:mi:ss tzd'),
    MYDT, MYVAL 
FROM MYTAB;

тогда вы должны увидеть полную картину.

0 голосов
/ 19 декабря 2018

Я узнал, что

SELECT cast(MYDT at time zone 'UTC' as date) FROM ...

соответствует моему запросу.Теперь все в UTC, и я могу различить.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...