【问题标题】:Unix Timestamp to ISO-8601 StringUnix 时间戳到 ISO-8601 字符串
【发布时间】:2016-11-16 17:46:42
【问题描述】:

我有一个名为 ACQDATA 的 Oracle 表,其中包含一个字段 READDATETIME,我在其中将 Unix 时间戳存储为 INTEGER (NUMBER(38)) 类型。

SQL> select READDATETIME from ACQDATA where ID=1000;

READDATETIME
____________
   1.4793E+12

我需要将该值选择为 ISO-8601 字符串 (YYYY-MM-DDTHH:MM:SS.mmm):

SQL> select READDATETIME from ACQDATA where ID=1000;

READDATETIME
-------------------
   1.4793E+12

我尝试使用TO_CHAR 转换它,但结果很混乱:

   SQL> select TO_CHAR(TO_DATE('1970-01-01','YYYY-MM-DD') + NUMTODSINTERVAL(READDATETIME, 'SECOND'), 'YYYY-MM-DD HH24:MI:SS') from ACQDATA where ID=1000;

Error at line 1:
ORA-01873: the leading precision of the interval is too small    

帮助表示赞赏。

【问题讨论】:

  • 一年大约是 3 * 10^7 秒。如果readDateTime 是自 1970 年 1 月 1 日以来的秒数,这意味着它大约是 1970 年之后的 10^5 年或未来 100,000 年(给予或接受)。这似乎不太可能。如果readDateTime 是毫秒数,那么它是 1970 年 46 年后的日期,所以它应该是 2016 年。
  • 那么,readdatetime 以秒为单位吗?如果是这样,只需写to_date(...) + readdatetime/86400(其中第一项是 1/1/1970);如果贾斯汀怀疑它以毫秒为单位,则除以 86400000。然后您可以根据需要转换为 char(在大多数情况下不应该)。
  • readdatetime 是自 1970 年 1 月 1 日以来的 Unix 时间戳,以毫秒为单位。已在原帖中更正。

标签: oracle timestamp iso8601


【解决方案1】:

Alex 的回答并不完全正确。 Unix 时间戳始终基于 1970-01-01 00:00:00 UTC

除非您的会话在 UTC 时区运行,否则精确的解决方案将是这样的:

select 
   TO_CHAR((TIMESTAMP '1970-01-01 00:00:00 UTC' + readdatetime/1000 * INTERVAL '1' SECOND) AT LOCAL, 'YYYY-MM-DD"T"HH24:MI:SS.FF3')
from ACQDATA where ID=1000;

select 
   TO_CHAR((TIMESTAMP '1970-01-01 00:00:00' AT TIME ZONE 'UTC' + readdatetime/1000 * INTERVAL '1' SECOND) AT LOCAL, 'YYYY-MM-DD"T"HH24:MI:SS.FF3')
from ACQDATA where ID=1000;

或者如果你更喜欢函数而不是文字:

select 
   TO_CHAR((TO_TIMESTAMP_TZ('1970-01-01 00:00:00 UTC', 'YYYY-MM-DD HH24:MI:SS TZR') + numtodsinterval(readdatetime/1000, 'SECOND')) AT LOCAL, 'YYYY-MM-DD"T"HH24:MI:SS.FF3')
from ACQDATA where ID=1000;

【讨论】:

  • 谢谢。现在好多了。
【解决方案2】:

您的readdatetime 似乎以毫秒为单位。 Oracle 日期算法基于天数,因此您需要将该数字转换为它所代表的天数;一天是 86400 秒,所以是 86400000 毫秒:

with acqdata (id, readdatetime) as (
  select 1000, 1479318995000 from dual
)
select to_char(date '1970-01-01' + (READDATETIME/86400000), 'YYYY-MM-DD"T"HH24:MI:SS')
from ACQDATA where ID=1000;

TO_CHAR(DATE'1970-0
-------------------
2016-11-16T17:56:35

T 添加为a character literal

SQL Developer 默认以科学计数法显示大的数字。您可以使用set numformat 更改该默认值,或使用to_char() 显示整个值:

select readdatetime, to_char(readdatetime, '9999999999999') as string
from ACQDATA where ID=1000;

READDATETIME STRING        
------------ --------------
  1.4793E+12  1479318995000

如果您的值有小数秒,因此最后三位数字不是零,您可以将日期转换为时间戳并添加小数剩余部分;这也增加了 UTC 'Z' 指示器的乐趣:

with acqdata (id, readdatetime) as (
  select 1000, 1479300462063 from dual
)
select to_char(cast(date '1970-01-01' + (readdatetime/86400000) as timestamp)
    + numtodsinterval(remainder(readdatetime, 1000)/1000, 'SECOND'),
  'YYYY-MM-DD"T"HH24:MI:SS.FF3"Z"')
from acqdata where id=1000;

TO_CHAR(CAST(DATE'1970-01-01'+
------------------------------
2016-11-16T12:47:42.063Z

或者没有中间 date 值,从时间戳文字开始:

with acqdata (id, readdatetime) as (
  select 1000, 1479300462063 from dual
)
select to_char(timestamp '1970-01-01 00:00:00'
  + numtodsinterval(readdatetime/1000, 'SECOND'),
  'YYYY-MM-DD"T"HH24:MI:SS.FF3"Z"')
from acqdata where id=1000;

TO_CHAR(TIMESTAMP'1970-0
------------------------
2016-11-16T12:47:42.063Z

正如@Wernfried 所说,最好明确表明纪元时间是从 UTC 开始的:

alter session set time_zone='America/New_York';

with acqdata (readdatetime) as (
  select 1479300462063 from dual
  union all select 1467331200000 from dual
  union all select 1467648000000 from dual
)
select readdatetime,
  to_char(timestamp '1970-01-01 00:00:00' + numtodsinterval(readdatetime/1000, 'SECOND'),
    'YYYY-MM-DD"T"HH24:MI:SS.FF3') as implicit,
  to_char(cast(timestamp '1970-01-01 00:00:00' as timestamp with time zone)
    + numtodsinterval(readdatetime/1000, 'SECOND'),
    'YYYY-MM-DD"T"HH24:MI:SS.FF3TZH:TZM') as local_offset,
  to_char(timestamp '1970-01-01 00:00:00 UTC' + numtodsinterval(readdatetime/1000, 'SECOND'),
    'YYYY-MM-DD"T"HH24:MI:SS.FF3TZH:TZM') as utc_offset,
  to_char(timestamp '1970-01-01 00:00:00 UTC' + numtodsinterval(readdatetime/1000, 'SECOND'),
    'YYYY-MM-DD"T"HH24:MI:SS.FF3TZR') as utc
from acqdata;

  READDATETIME IMPLICIT                LOCAL_OFFSET                  UTC_OFFSET                    UTC                       
-------------- ----------------------- ----------------------------- ----------------------------- --------------------------
 1479300462063 2016-11-16T12:47:42.063 2016-11-16T12:47:42.063-05:00 2016-11-16T12:47:42.063+00:00 2016-11-16T12:47:42.063UTC
 1467331200000 2016-07-01T00:00:00.000 2016-07-01T01:00:00.000-04:00 2016-07-01T00:00:00.000+00:00 2016-07-01T00:00:00.000UTC
 1467648000000 2016-07-04T16:00:00.000 2016-07-04T17:00:00.000-04:00 2016-07-04T16:00:00.000+00:00 2016-07-04T16:00:00.000UTC

【讨论】:

  • 快到了。结果字符串中的毫秒数和T 在哪里? ISO-8601 定义了YYYY-MM-DDTHH:MM:SS.mmm。结果字符串只包含没有 T 的秒数。
  • 我已经添加了 T。你的值是否真的有毫秒值,或者最后三位数字总是 000?
  • 它有特定的毫秒值。示例值为1479300462063(063 毫秒)。
  • @Mendes - 好的,我已经更新以展示如何处理它。
  • 是的!非常非常有帮助的亚历克斯。谢谢。
猜你喜欢
  • 2013-03-11
  • 2012-02-13
  • 1970-01-01
  • 2023-04-11
  • 2021-03-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-12-06
相关资源
最近更新 更多