如何在 Oracle 中将 Unix 时间戳转换为本地日期?

时间:2021-06-25 18:11:53

标签: oracle timezone unix-timestamp

互联网,请帮忙!

我正在使用 Oracle DB v 12c,其中发票开具时间在类型为 NUMBER 的列中存储为 unix 时间戳,还有一个 VARCHAR2(128) 列,它定义发票开具的时区,例如“欧洲/伦敦'。

因此可以像这样查询在本地日期“2020-10-10”开具的发票:

SELECT inv.invoiceID
FROM invoices inv
WHERE TO_CHAR(
                  FROM_TZ(timestamp '1970-01-01 00:00:00'
                              + NUMTODSINTERVAL(inv.INVOICETIME, 'SECOND'), 'UTC') AT TIME ZONE inv.timezoneName,
                  'YYYY-MM-DD') = '2020-10-10';

但不幸的是,此查询不可索引。

问题是,这不起作用:

CREATE INDEX Invoices_InvoiceDate ON Invoices (
        TO_CHAR(
                  FROM_TZ(timestamp '1970-01-01 00:00:00'
                              + NUMTODSINTERVAL(INVOICETIME, 'SECOND'), 'UTC') AT TIME ZONE timezoneName,
                  'YYYY-MM-DD')
                   );

错误: ORA-01743: 只能索引纯函数

因为使用了“AT TIME ZONE”,这个算子的“纯版本”是什么?或者换句话说 - 如何在 Oracle 中将 Unix 时间戳属性转换为本地日期?

1 个答案:

答案 0 :(得分:0)

正如@mathguy 已经解释的那样,您不能在特定时区创建索引:

CREATE INDEX IND_INVOICETIME_LOCAL ON INVOICE (
    (TIMESTAMP '1970-01-01 00:00:00 UTC' + NUMTODSINTERVAL(INVOICETIME, 'SECOND')) AT TIME ZONE timezoneName
);

ORA-01743: only pure functions can be indexed

您可以做的是在 UTC 时间创建索引:

CREATE INDEX IND_INVOICETIME_UTC ON INVOICE (
    TIMESTAMP '1970-01-01 00:00:00 UTC' + NUMTODSINTERVAL(INVOICETIME, 'SECOND')
);

那么条件是这样的:

WHERE TIMESTAMP '1970-01-01 00:00:00 UTC' + NUMTODSINTERVAL(INVOICETIME, 'SECOND') 
   BETWEEN FROM_TZ(TIMESTAMP '2020-10-10 00:00:00', timezoneName) 
       AND FROM_TZ(TIMESTAMP '2020-10-10 23:59:59', timezoneName)

Oracle 总是在 UTC 时间比较 TIMESTAMP WITH TIME ZONE 值。 注意,当您有一个TIMESTAMP WITH TIME ZONE 列并在其上创建索引时,Oracle 会添加一个虚拟列SYS_EXTRACT_UTC(...),并在此虚拟​​列上创建索引。正确使用此类索引/列可能具有挑战性,请参阅 https://tonyhasler.wordpress.com/2010/09/04/tonys-tirade-against-timestamp-with-time-zone/

我认为正确的用法是这样的:

WHERE SYS_EXTRACT_UTC(TIMESTAMP '1970-01-01 00:00:00 UTC' + NUMTODSINTERVAL(INVOICETIME, 'SECOND') ) 
    BETWEEN SYS_EXTRACT_UTC(FROM_TZ(TIMESTAMP '2020-10-10 00:00:00', inv.timezoneName)) 
        AND SYS_EXTRACT_UTC(FROM_TZ(TIMESTAMP '2020-10-10 23:59:59', inv.timezoneName))