8

Oracle 中是否有一种平滑的方法可以在当前时刻(当我执行调用时)获取 SESSIONTIMEZONE 和 DBTIMEZONE 之间的数值差异?

例如:

SELECT SESSIONTIMEZONE, DBTIMEZONE FROM DUAL;

回报:

+04:00  +07:00

所以,我需要某种函数,通过使用给定参数调用它,我得到这两个值之间的差异。

对于上面的例子:

SELECT get_numeric_offset(SESSIONTIMEZONE, DBTIMEZONE) FROM DUAL;

将返回 -3(符号很重要)。

当然,我可以自己编写这个函数,方法是处理字符串并解析它们,然后进行一些算术运算或做类似的事情(我仍然不认为这是一个非常顺利的解决方案:

SELECT (
        CAST(SYSTIMESTAMP AT TIME ZONE SESSIONTIMEZONE AS DATE) - 
        CAST(SYSTIMESTAMP AT TIME ZONE DBTIMEZONE AS DATE)
       )*24 
 FROM DUAL;

也许我错过了一些东西,而 Oracle 实际上提供了一种计算两个给定时区之间差异的方法?

4

2 回答 2

16

关于使用 SESSIONTIMEZONE 的一点警告

您的第一个示例不是防弹的,因为会话时区可以设置为以下任一:

  • 操作系统本地时区 ( 'OS_TZ')
  • 数据库时区 ( 'DB_TZ')
  • 与 UTC 的绝对偏移量(例如,'-05:00'
  • 时区区域名称(例如,'Europe/London'

看看这里发生了什么:

SELECT SESSIONTIMEZONE, DBTIMEZONE FROM DUAL;

SESSIONTIMEZONE  |  DBTIMEZONE
         +04:00  |      +07:00

ALTER SESSION SET TIME_ZONE='Europe/Paris';

SELECT SESSIONTIMEZONE, DBTIMEZONE FROM DUAL;

SESSIONTIMEZONE  |  DBTIMEZONE
   Europe/Paris  |      +07:00

在您的函数中解析“Europe/Paris”字符串将会变得更加困难......您应该使用该TZ_OFFSET函数,以确保您始终获得相同的±HH:MM格式。

ALTER SESSION SET TIME_ZONE='Europe/Paris';

SELECT DBTIMEZONE, SESSIONTIMEZONE, TZ_OFFSET(SESSIONTIMEZONE) FROM DUAL;

 DBTIMEZONE | SESSIONTIMEZONE | TZ_OFFSET(SESSIONTIMEZONE)
     +07:00 |    Europe/Paris |                     +02:00

关于您的第二个解决方案

我想我更喜欢你的第二个解决方案。您可以通过使用CURRENT_DATE而不是缩短它CAST(SYSTIMESTAMP AT TIME ZONE SESSIONTIMEZONE AS DATE),它们是等效的:

SELECT (
        CURRENT_DATE - 
        CAST(SYSTIMESTAMP AT TIME ZONE DBTIMEZONE AS DATE)
       )*24 
 FROM DUAL;

从技术上讲,您甚至可以做到这一点!

SELECT (CURRENT_DATE - SYSDATE) * 24 
 FROM DUAL;

但实际上SYSDATE不能保证与DBTIMEZONE. SYSDATE始终绑定到底层操作系统的时区,而DBTIMEZONE一旦服务器启动就可以更改。

而且我在扯皮的同时还要提醒你,某些国家/地区使用的偏移量不是整数,例如伊朗标准时间是UTC+03:30,缅甸时间是UTC+06:30......我不知道你是否会遇到这在您的生产环境中,但我希望您对查询返回类似1.5...的可能性感到满意

于 2013-08-12T08:32:10.820 回答
6

你想要什么是不可能的,因为时区不是固定的,而是随着时间的推移而变化。因此,您无法计算两个时区之间的差异,而只能计算在某个时间点有效的差异。

时区随时间变化是很常见的。在TZ 数据库中,时区每年都会发生许多变化,例如更改时区的地方或更改夏令时的开始、结束或差异,或者在“随机”时间插入闰秒。

因此,要计算偏移量,您还必须给出一个特定的时间点,您希望为该时间点计算偏移量(即应该应用哪个版本的时区定义)。

考虑到这一点,就很清楚了,如何计算某个时间点两个时区之间的差异:

select 
  (TIMESTAMP '2012-06-30 22:00:00 US/Pacific') - (TIMESTAMP '2012-06-30 22:00:00 Europe/Berlin') as diff
from
    dual
;

DIFF
------------------------------
+000000000 09:00:00.000000000

当您拥有这些信息时,您必须决定您希望差异达到哪种精度。我特意选择了上面的日期,因为那天晚上插入了闰秒。所以可能在某个时间点,你会得到不完全是 9 小时,而是 9 小时 1 秒(或者是 9 小时减去 1 秒)的差异。

同样重要的是,您插入确切的位置而不是其他一些时间戳信息。位置可以更改为不同的时区。因此,您必须指定拖车位置和时间点才能获得正确的时区差异。

如果要舍入差异,可以使用EXTRACT(HOUR FROM ...)和访问其组件EXTRACT(MINUTE FROM ...)。如果想在第二级进行舍入,可以使用以下技巧:

select 
    (to_number(to_char(sys_extract_utc(TIMESTAMP '2012-07-01 01:00:00 US/Pacific'), 'SSSSSFF3')) 
    - to_number(to_char(sys_extract_utc(TIMESTAMP '2012-07-01 01:00:00 Europe/Berlin'), 'SSSSSFF3')))/1000 as diff_s
from 
    dual
;

    DIFF_S
----------
    -54000
于 2013-08-12T13:12:38.067 回答