TO_TIMESTAMP_TZ 和 CAST AS TIMESTAMP WITH LOCAL TIME ZONE 在 Oracle DB 中的不同结果

Different results for TO_TIMESTAMP_TZ and CAST AS TIMESTAMP WITH LOCAL TIME ZONE in Oracle DB

我有一个 Oracle 11g 数据库,我正在尝试使用 TIMESTAMP 数据类型(尤其是从不包含时区的传入数据创建的数据类型)处理 DST 的方式。我发现使用 TO_TIMESTAMP_TZ() 与 CAST(DATE as TIMESTAMP WITH LOCAL TIME ZONE) 时的行为有所不同,我无法解释。我希望生成的数据类型是一个分区时间戳,无论它是如何创建的,对它的任何操作都是相同的,但事实并非如此。

知道为什么在使用 CAST 选项时,INTERVAL 数学中没有考虑秋季 DST 更改创建的 "extra" 小时吗?我希望下面查询的两行是相同的。

查询:

select to_timestamp_tz('11/1/2015 1:00 AM US/Pacific', 'MM/DD/YYYY HH:mi AM TZR') + interval '2' hour as DST_PLUS_2, -- this is a post DST jump time
       to_timestamp_tz('11/1/2015 12:59 AM US/Pacific', 'MM/DD/YYYY HH:mi AM TZR') + interval '2' hour as PRE_DST_PLUS_2, -- this is a pre DST jump time
       to_char(to_timestamp_tz('11/1/2015 1:00 AM US/Pacific', 'MM/DD/YYYY HH:mi AM TZR'), 'TZR') as TZR
  from dual
union all
select cast(to_date('11/1/2015 1:00 AM', 'MM/DD/YYYY HH:mi AM') as timestamp with local time zone) + interval '2' hour as DST_PLUS_2,
       cast(to_date('11/1/2015 12:59 AM', 'MM/DD/YYYY HH:mi AM') as timestamp with local time zone) + interval '2' hour as PRE_DST_PLUS_2,
       to_char(cast(to_date('11/1/2015 1:00 AM', 'MM/DD/YYYY HH:mi AM') as timestamp with local time zone), 'TZR') as TZR
  from dual;

结果:

DST_PLUS_2                                  PRE_DST_PLUS_2                              TZR
01-NOV-15 03.00.00.000000000 AM US/PACIFIC  01-NOV-15 01.59.00.000000000 AM US/PACIFIC  US/PACIFIC
01-NOV-15 03.00.00.000000000 AM US/PACIFIC  01-NOV-15 02.59.00.000000000 AM US/PACIFIC  US/PACIFIC

看起来这归结为 TO_TIMESTAMP_TZ 尝试从日期推断 TIMESTAMP 的 TZD 元素,而 CAST AS TIMESTAMP WITH LOCAL TIME ZONE 将采用数据库的当前 TZD,而不管日期是什么时候。或者他们只是有不同的默认行为来选择凌晨 1 点时间(前或 post 时间更改)。这种行为差异导致结果小时相差一个小时。

谢谢 ruudvan!

查询:

select to_char(to_timestamp_tz('11/1/2015 1:00 AM US/Pacific', 'MM/DD/YYYY HH:mi AM TZR'), 'TZD') as TZD_TO_TS_TZ_1,
       to_char(cast(to_date('11/1/2015 1:00 AM', 'MM/DD/YYYY HH:mi AM') as timestamp with local time zone), 'TZD') as TZD_CAST_1,
       to_char(to_timestamp_tz('11/1/2015 12:00 AM US/Pacific', 'MM/DD/YYYY HH:mi AM TZR'), 'TZD') as TZD_TO_TS_TZ_12,
       to_char(cast(to_date('11/1/2015 12:00 AM', 'MM/DD/YYYY HH:mi AM') as timestamp with local time zone), 'TZD') as TZD_CAST_12
  from dual;

结果:

TZD_TO_TS_TZ_1  TZD_CAST_1  TZD_TO_TS_TZ_12 TZD_CAST_12
PST             PDT         PDT             PDT

数据类型TIMESTAMP WITH TIME ZONE存储时区信息。当您插入 11/1/2015 1:00 AM US/Pacific 时,这就是存储的内容,这就是您在 select 时得到的内容。

数据类型TIMESTAMP WITH TIME LOCAL ZONE也存储时区信息。但是,在当前用户会话时区 SESSIONTIMEZONE 中显示的值始终 。原则上 Oracle 总是将值放在 ({TIMESTAMP WITH TIME ZONE} value) AT TIME ZONE SESSIONTIMEZONE

DATE 不存储任何时区信息。如果你 运行 CAST({DATE value} as timestamp with local time zone) 那么 Oracle 运行 基本上是 FROM_TZ({DATE value}, SESSIONTIMEZONE) as timestamp with local time zone)。 IE。它将当前用户会话时区(可能与 US/Pacific 不同)附加到 DATE 值,然后根据需要转换时区。

所以您的查询结果实际上取决于您当前的会话时区 SESSIONTIMEZONE。请注意,这可能是 US/Pacific+07:00 / +08:00。如果它设置为 US/Pacific 则考虑夏令时。对于像 +07:00+08:00 这样的 UTC 偏移,时区是固定的,即不考虑夏令时。