在Oracle SQL中将Unix纪元转换为带时区的日期/时间戳

时间:2016-06-28 23:09:23

标签: oracle datetime timezone

我试图编写SQL表达式,将unix时期(自1970/1/1以来的秒数)写入特定时区的本地时间,并从中提取小时值。在此之后研究解决方案之后,我仍然不确定如何处理时区。请注意,我尽量避免使用NEW_TIME()函数,因为它只占用时区首字母缩略词的有限子集,而不是全时区名称。

select 
(TIMESTAMP '1970-01-01 00:00:00' AT TIME ZONE 'UTC' + numtodsinterval(1464820200,'second')) as ts_utc,
(TIMESTAMP '1970-01-01 00:00:00' AT TIME ZONE 'America/Los_Angeles' + numtodsinterval(1464820200,'second')) as ts_la,
(DATE '1970-01-01' + numtodsinterval(1464820200,'second')) as date_utc,
FROM_TZ(CAST(DATE '1970-01-01' + numtodsinterval(1464820200,'second') as TIMESTAMP), 'UTC') AT TIME ZONE 'America/New_York' as date_ny,
FROM_TZ(CAST(DATE '1970-01-01' + numtodsinterval(1464820200,'second') as TIMESTAMP), 'UTC') AT TIME ZONE 'America/Los_Angeles' as date_la, -- this value is correct
EXTRACT(hour from FROM_TZ(CAST(DATE '1970-01-01' + numtodsinterval(1464820200,'second') as TIMESTAMP), 'UTC') AT TIME ZONE 'America/New_York') as hour,
EXTRACT(TIMEZONE_OFFSET from FROM_TZ(CAST(DATE '1970-01-01' + numtodsinterval(1464820200,'second') as TIMESTAMP), 'UTC') AT TIME ZONE 'America/New_York') as tz_offset,
NEW_TIME((DATE '1970-01-01' + numtodsinterval(1464820200,'second')), 'GMT', 'EDT') as date_edt -- this value is correct
from dual;

,结果是

TS_UTC  TS_LA   DATE_UTC    DATE_NY DATE_LA HOUR    TZ_OFFSET   DATE_EDT
2016-06-01 23:30:00.0   2016-06-01 23:30:00.0   2016-06-01 22:30:00.0   2016-06-01 15:30:00.0   2016-06-01 15:30:00.0   22  <UnknownType (-104)>    2016-06-01 18:30:00.0

结果有几个问题

  1. 第1列:从时间戳开始,该值比使用DATE
  2. 的时间少1小时
  3. 第2列:在America / Los_Angeles中创建此时间戳会产生与UTC相同的值
  4. 第4列:将时间戳转换为America / New_York会产生与America / Los_Angeles相同的值
  5. 第6列:提取的小时是第3列的UTC小时
  6. 第7列:无法全部提取时区偏移量,导致“未知类型”
  7. 执行此操作的正确Oracle SQL是什么?

3 个答案:

答案 0 :(得分:0)

我提出了一个或多或少的kackish解决方案 - 使用TO_CHAR()函数将其首先转换为字符串,然后将字符串转换为数字。

TO_NUMBER(TO_CHAR(FROM_TZ(CAST(DATE '1970-01-01' + numtodsinterval(1464820200,'second') as TIMESTAMP), 'UTC') AT TIME ZONE 'America/New_York', 'HH24')) as date_ny_int

FROM_TZ()AT TIME ZONE 也按预期工作,但SQL工具(甚至基于JDBC的工具)将输出一个本地化时区的字符串;因此,也应该使用TO_CHAR()转换为字符串输出。

答案 1 :(得分:0)

  1. 我运行了你的代码,在我的机器上它显示在第1列的下午22:30,不知道为什么你得到23:30 PM(实际上我不相信你)。实际上,你可能想知道为什么你在第2栏中没有得到相同的答案(22:30 PM);这是因为命名时区包括夏令时调整,这不是UTC约定的一部分(因此第一列不受影响)。

  2. 和3.为时间戳添加秒数将为您提供新的时间戳。事实上,秒数是一个&#34; UNIX时代&#34;在你的心中没有意义; SQL如何知道您想要将UTC转换/转换为您想要的任何时区?

  3. 请参阅我的答案1.提取的小时数为22,这是正确的。

  4. 在我的机器上,时区偏移量被精确提取,时间为-4小时。

  5. 您使用的是哪个版本的Oracle?

答案 2 :(得分:0)

我已经发布了here一些方法,可将纳秒转换为时间戳,并将时间戳转换为纳秒。这些方法不受时区的影响,并且具有纳秒级的精度。

您需要将时区设置为看到“ -06:00”。

SELECT (TIMESTAMP '1970-01-01 00:00:00 UTC' + numtodsinterval(
    1598434427263 --Replace line with desired milliseconds
/ 1000, 'SECOND')) AT TIME ZONE '-06:00' AS TIMESTAMP FROM dual;

TIMESTAMP
26/08/20 09:33:47,263000000 UTC