在Oracle中将本地日期时间(带时区)转换为Unix时间戳
我目前有一个SQL查询,它从数据库中的Unix在Oracle中将本地日期时间(带时区)转换为Unix时间戳,oracle,Oracle,我目前有一个SQL查询,它从数据库中的Unix时间戳列返回正确的本地日期时间 下面是一个使用特定时间戳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.00
时间戳
列返回正确的本地日期时间
下面是一个使用特定时间戳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时间戳
以前有人遇到过这种情况吗?您可以将带有时区的时间戳转换为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
如果起始时间戳中有毫秒,这也会保留毫秒(在保留毫秒的同时,会从“Unix”时间转换):
然后要转换回:
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
如果开始时间戳的精度高于此值,则需要截断(或round/floor/ceil/cast)以避免产生非整数结果;此版本仅截断提取的毫秒部分:
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
我忘记了闰秒的差异;如果您需要/想要处理此问题,.您可以将时区的时间戳转换为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
如果起始时间戳中有毫秒,这也会保留毫秒(在保留毫秒的同时,会从“Unix”时间转换):
然后要转换回:
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
如果开始时间戳的精度高于此值,则需要截断(或round/floor/ceil/cast)以避免产生非整数结果;此版本仅截断提取的毫秒部分:
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
我忘记了闰秒的差异;如果您需要/想要处理这个问题,那么主要的问题是Oracle有两种方法(至少)将秒数转换为一天到一秒的时间间隔-使用函数或对时间间隔文字进行简单的算术运算-但没有直接的方法来做相反的操作
在下面的两个查询中,首先我将演示如何将UNIX时间戳(从纪元开始以毫秒为单位)转换为Oracle时间戳,而不会丢失毫秒。(请参阅我在您的问题下的评论,其中我指出您的方法将丢失毫秒。)然后我将演示如何反转该过程
和您一样,我忽略了“UTC时间戳”和“Unix时间戳”之间的差异,这是由于“Unix时间戳”忽略了闰秒造成的。您的企业必须确定这是否重要
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
从时区到Unix时间戳的Oracle时间戳(保留毫秒):
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
主要问题是Oracle有两种方法(至少)可以将秒数转换为从天到秒的时间间隔——要么使用函数,要么对时间间隔文字进行简单的算术运算——但没有直接的方法可以实现相反的转换
在下面的两个查询中,首先我将演示如何将UNIX时间戳(从纪元开始以毫秒为单位)转换为Oracle时间戳,而不会丢失毫秒。(请参阅我在您的问题下的评论,其中我指出您的方法将丢失毫秒。)然后我将演示如何反转该过程
和您一样,我忽略了“UTC时间戳”和“Unix时间戳”之间的差异,这是由于“Unix时间戳”忽略了闰秒造成的。您的企业必须确定这是否重要
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
从时区到Unix时间戳的Oracle时间戳(保留毫秒):
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
您的Unix时间戳是否可以有几分之一秒?如果可以,那么您将其转换为Oracle时间戳的直接过程已经不正确。这是因为Oracle中的date
数据类型(这是在date
literal中添加“天数”时得到的)丢弃了一秒的分数(如毫秒)。Unix时间戳的可能副本可以有一秒的分数吗?如果可以,那么您将其转换为Oracle时间戳的直接过程已经不正确。这是因为Oracle中的date
数据类型(这是在date
literal中添加“天数”时得到的)丢弃了一秒的分数(如毫秒)。您是正确的。我们的数据库支持以毫秒为单位的插入。我们用它来记录工厂设备发出的信号。工程人员可以轻松地将数据采集设置为>1 Hz。我主要是查询这些信号的汇总统计数据,所以以秒为单位的括号应该可以。但是,我应该让我的查询遵循DB的标准。谢谢您的帮助。您也可以将时间戳文字与时区一起使用,即TIMESTAMP'2018-10-19 09:12:47.186 America/Denver'
而不是从TZ(…)
或到TIMESTAMP_TZ(…)
您是对的。我们的数据库支持以毫秒为单位的插入。我们用它来记录工厂设备发出的信号。工程人员可以轻松地将数据采集设置为>1 Hz。我主要是查询这些信号的汇总统计数据,所以以秒为单位的括号应该可以。但是,我应该让我的查询遵循DB的标准。谢谢你