Oracle DB 中 TO_TIMESTAMP_TZ 和 CAST AS TIMESTAMP WITH LOCAL TIME ZONE 的不同结果
Posted
技术标签:
【中文标题】Oracle DB 中 TO_TIMESTAMP_TZ 和 CAST AS TIMESTAMP WITH LOCAL TIME ZONE 的不同结果【英文标题】:Different results for TO_TIMESTAMP_TZ and CAST AS TIMESTAMP WITH LOCAL TIME ZONE in Oracle DB 【发布时间】:2015-03-09 18:14:17 【问题描述】:我有一个 Oracle 11g 数据库,我正在尝试使用 TIMESTAMP 数据类型(尤其是从不包含时区的传入数据创建的数据类型)来试验如何处理 DST。在使用 TO_TIMESTAMP_TZ() 与 CAST(DATE as TIMESTAMP WITH LOCAL TIME ZONE) 时,我看到了我无法解释的行为差异。我希望生成的数据类型是分区时间戳,无论它是如何创建的,并且对其进行的任何操作都是相同的,但事实并非如此。
知道为什么在使用 CAST 选项时,由秋季 DST 更改创建的“额外”小时没有被考虑到 INTERVAL 数学中吗?我希望下面查询的两行是相同的。
查询:
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
【问题讨论】:
一个提示可以帮助您自己弄清楚 - 而不是选择 TZR,SELECT
TZD。而不是只为凌晨 1 点选择 TZD,还为凌晨 12:59 选择 TZD。对两行都执行此操作,这将告诉您选择的时间戳是 PST,哪个是 PDT。
【参考方案1】:
看起来这归结为这样一个事实,即 TO_TIMESTAMP_TZ 尝试从日期推断 TIMESTAMP 的 TZD 元素,而 CAST AS TIMESTAMP WITH LOCAL TIME ZONE 将采用数据库的当前 TZD,而不管日期是什么时候。或者他们只是有不同的默认行为来选择要选择的凌晨 1 点时间(时间更改之前或之后)。这种行为差异导致结果小时相差一个。
谢谢鲁德万!
查询:
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
【讨论】:
你明白了。WITH LOCAL TIMEZONE
将采用当前的 TZD 设置。【参考方案2】:
数据类型TIMESTAMP WITH TIME ZONE
存储时区信息。当您插入 11/1/2015 1:00 AM US/Pacific
时,这就是存储的内容,这就是您选择它时得到的内容。
数据类型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 偏移量,时区是固定的,即不考虑夏令时。
【讨论】:
以上是关于Oracle DB 中 TO_TIMESTAMP_TZ 和 CAST AS TIMESTAMP WITH LOCAL TIME ZONE 的不同结果的主要内容,如果未能解决你的问题,请参考以下文章
Oracle SQL to_date & to_timestamp ORA-01858: 在需要数字的地方发现了一个非数字字符 & ORA-01850: 小时必须在 0 到 23 之间