Преобразование локальной даты и времени (с часовым поясом) в метку времени Unix в Oracle - PullRequest
0 голосов
/ 20 октября 2018

В настоящее время у меня есть SQL-запрос, который возвращает правильный локальный DATETIME из столбца Unix TIMESTAMP в нашей БД.

Вот пример использования определенного TIMESTAMP из 1539961967000:

SELECT FROM_TZ(CAST(DATE '1970-01-01' + 1539961967000 * (1/24/60/60/1000) AS TIMESTAMP), 'UTC') AT TIME ZONE 'America/Denver' DATETIME
FROM dual;

, который возвращает:

DATETIME
19-OCT-18 09.12.47.000000000 AM AMERICA/DENVER

Мне трудно отменить этот запрос, чтобы вернуть Unix TIMESTAMP, начиная с локального DATETIME.

Кто-нибудь когда-нибудь сталкивался с этим раньше?

Ответы [ 2 ]

0 голосов
/ 20 октября 2018

Основная проблема заключается в том, что у Oracle есть два (как минимум) способа преобразования количества секунд в интервал день-секунда - либо с помощью функции, либо с помощью простой арифметической операции над литералом интервала - но без прямого способачтобы сделать обратное.

В двух приведенных ниже запросах сначала я покажу, как преобразовать метку времени UNIX (в миллисекундах с начала эпохи) в метку времени Oracle, не теряя миллисекунд.(См. Мой комментарий под вашим Вопросом, где я указываю, что ваш метод потеряет миллисекунды.) Затем я покажу, как отменить процесс.

Как и вы, я игнорирую разницу между «отметкой времени в UTC» и «Unix timestamp », вызванное« Unix timestamp », игнорирующим високосные секунды.Ваша компания должна определить, важно ли это.

метка времени Unix для метки времени Oracle с часовым поясом (с сохранением миллисекунд) :

with
  inputs (unix_timestamp) as (
    select 1539961967186 from dual
  )
select from_tz(timestamp '1970-01-01 00:00:00' 
               + interval '1' second * (unix_timestamp/1000), 'UTC')
                   at time zone 'America/Denver' as oracle_ts_with_timezone
from   inputs
;

ORACLE_TS_WITH_TIMEZONE               
--------------------------------------
2018-10-19 09:12:47.186 America/Denver

метка времени Oracle со временемметка времени в Unix (с сохранением миллисекунд) :

with
  sample_data (oracle_ts_with_timezone) as (
    select to_timestamp_tz('2018-10-19 09:12:47.186 America/Denver', 
                           'yyyy-mm-dd hh24:mi:ss.ff tzr')           from dual
  )
select ( extract(second from ts) 
         + (trunc(ts, 'mi') - date '1970-01-01') * (24 * 60 * 60)
       ) * 1000 as unix_timestamp
from   ( select cast(oracle_ts_with_timezone at time zone 'UTC' 
                     as timestamp) as ts
         from   sample_data
       )
;

  UNIX_TIMESTAMP
----------------
   1539961967186
0 голосов
/ 20 октября 2018

Вы можете преобразовать свою временную метку с часовым поясом в UTC, а затем вычесть время из этого:

select timestamp '2018-10-19 09:12:47.0 AMERICA/DENVER'
  - timestamp '1970-01-01 00:00:00.0 UTC' as diff
from dual;

, что дает вам тип данных с интервалом:

DIFF                  
----------------------
+17823 15:12:47.000000

Затем вы можетеизвлечь из этого элементы и умножить каждый элемент на соответствующий коэффициент, чтобы преобразовать его в миллисекунды (т. е. для дней 60 * 60 * 24 * 1000);и затем сложите их вместе:

select extract(day from diff) * 86400000
  + extract(hour from diff) * 3600000
  + extract(minute from diff) * 60000
  + extract(second from diff) * 1000 as unixtime
from (
  select timestamp '2018-10-19 09:12:47.0 AMERICA/DENVER'
    - timestamp '1970-01-01 00:00:00.0 UTC' as diff
  from dual
);

            UNIXTIME
--------------------
       1539961967000

db <> fiddle

Это также сохраняет миллисекунды, если они есть у начальной временной метки (это конвертируется из Unix)время их сохранения):

select (timestamp '1970-01-01 00:00:00.0 UTC' + (1539961967567 * interval '0.001' second))
  at time zone 'America/Denver' as denver_time
from dual;

DENVER_TIME                                 
--------------------------------------------
2018-10-19 09:12:47.567000000 AMERICA/DENVER

, а затем преобразовать обратно:

select extract(day from diff) * 86400000
  + extract(hour from diff) * 3600000
  + extract(minute from diff) * 60000
  + extract(second from diff) * 1000 as unixtime
from (
  select timestamp '2018-10-19 09:12:47.567 AMERICA/DENVER'
    - timestamp '1970-01-01 00:00:00.0 UTC' as diff
  from dual
);

            UNIXTIME
--------------------
       1539961967567

db <> fiddle

Если ваша начальная временная метка имеетчем выше точность, то вам нужно будет усечь (или округлить / пол / потолок / приведение), чтобы избежать нецелого результата;эта версия просто обрезает извлеченную часть в миллисекундах:

select diff,
  extract(day from diff) * 86400000
  + extract(hour from diff) * 3600000
  + extract(minute from diff) * 60000
  + trunc(extract(second from diff) * 1000) as unixtime
from (
  select timestamp '2018-10-19 09:12:47.123456789 AMERICA/DENVER'
    - timestamp '1970-01-01 00:00:00.0 UTC' as diff
  from dual
);

DIFF                                  UNIXTIME
------------------------- --------------------
+17823 15:12:47.123456789        1539961967123

Без этого обрезания (или эквивалентного) вы бы получили 1539961967123.456789.


Я бы забыл орасхождение в високосных секундах;если вам нужно / хотите справиться с этим, см. этот ответ .

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