将本地日期时间(带有时区)转换为Oracle中的Unix时间戳

时间:2018-10-19 22:53:29

标签: oracle

我目前有一个SQL查询,该查询从数据库中的Unix DATETIME列返回正确的本地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

我很难逆转此查询以返回以本地TIMESTAMP开头的Unix DATETIME

以前有人遇到过吗?

2 个答案:

答案 0 :(得分:2)

您可以将带有时区的时间戳转换为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


我忘记了the秒的差异;如果您需要/想要处理此问题,请see this answer

答案 1 :(得分:2)

主要问题是Oracle有两种方法(至少)将秒数转换为秒间隔-使用函数或对间隔文字进行简单的算术运算-但没有直接方法反之。

在下面的两个查询中,我首先展示如何在不损失毫秒的情况下将UNIX时间戳(自大纪元以来的毫秒数)转换为Oracle时间戳。 (请参阅我在“问题”下的评论,其中指出您的方法将丢失毫秒。)然后,我将展示如何逆转该过程。

像您一样,我忽略了“ Unix时间戳”导致的“ UTC时间戳”和“ Unix时间戳”之间的差异,忽略了leap秒。您的企业必须确定这是否重要。

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