如何在夏时制开始时跳过的一小时内将时间戳存储在 Oracle 中
Posted
技术标签:
【中文标题】如何在夏时制开始时跳过的一小时内将时间戳存储在 Oracle 中【英文标题】:How to store a timestamp in Oracle that occurs within the hour skipped at the start of Daylight Savings 【发布时间】:2014-09-04 03:44:34 【问题描述】:我们的 Oracle 服务器在澳大利亚/悉尼时间运行。
我们计划在 UTC 中存储一些新日期。
例如,我们可能存储的日期之一是 2014 年 10 月 5 日凌晨 2:00。
但是,在悉尼,我们同时开始夏令时,这意味着从 2:00AM 到 2:59AM 的时间在当天不存在。
例如,2014 年 10 月 5 日发生的时间是:
01:58 01:59 03:00 03:01
麻烦的是,如果我尝试将时间2014-10-05 02:00
存储在数据库中,它会默默地转换为2014-10-05 03:00
我们没有在服务器上更改时区的选项,那么有什么方法可以将2014-10-05 02:00
存储在我们的数据库中吗?
编辑@mrjoltcola 的评论
我们的服务器正在运行时区设置(GMT +10) 堪培拉、墨尔本、悉尼
如果我运行命令select DBTIMEZONE from dual;
,输出是单个值+00:00
(这是意料之外的)。
我们的原始列是仅 TIMESTAMP 列,我没有在插入时提供任何时区详细信息。
为了进一步探索,我创建了一个测试表,如下所示:
create table TEST
(
ID number,
TS timestamp,
TS_TZ timestamp with time zone
)
然后我运行以下插入语句(带和不带时区):
insert into TEST
VALUES
(
1,
TIMESTAMP '2014-10-05 02:00:00 UTC',
TIMESTAMP '2014-10-05 02:00:00 UTC'
);
insert into TEST
VALUES
(
2,
TIMESTAMP '2014-10-05 02:00:00',
TIMESTAMP '2014-10-05 02:00:00'
);
产生结果:
+---+---------------------------------+--------------------------------------------------+
| 1 | 05/OCT/14 03:00:00.000000000 AM | 05/OCT/14 02:00:00.000000000 AM UTC |
+---+---------------------------------+--------------------------------------------------+
| 2 | 05/OCT/14 03:00:00.000000000 AM | 05/OCT/14 03:00:00.000000000 AM AUSTRALIA/SYDNEY |
+---+---------------------------------+--------------------------------------------------+
【问题讨论】:
什么是列类型?它是普通的 TIMESTAMP 还是带有 TIMEZONE 的 TIMESTAMP 或带有本地时区的 TIMESTAMP? @mrjoltcola 该列是一个普通的 TIMESTAMP。这是我们要添加的一个新字段,所以我刚刚将其更改为 TIMESTAMP WITH TIMEZONE,它似乎已经解决了这个问题。您想提供此评论作为答案,以便我可以将其标记为已接受的答案吗?非常感谢。 一旦我弄清楚到底发生了什么,我会很高兴的。如果您将原始时间戳存储在 UTC 中,我仍然对它们的调整方式感到困惑。如果您能告诉我您的 Oracle 数据库的时区设置是什么,以及您用于插入的 SQL 之前和之后,那将会有所帮助。您是否在插入中指定时区? 我已经编辑了原始问题以提供更多详细信息。最初,我只有一个 TIMESTAMP 字段,其中插入了一个不提供时区的内容。现在我使用 TIMESTAMP WITH TIME ZONE 字段并提供时区。 【参考方案1】:问题是,如果我尝试将时间 2014-10-05 02:00 存储在数据库中,它会默默地转换为 2014-10-05 03:00
在我的数据库中,我可以将会话时区设置为“澳大利亚/悉尼”,并且仍然将 2014-10-05 02:00:00 存储在一个普通的 TIMESTAMP 列中,而无需对其进行转换。但是,如果我将其更改为 TIMESTAMP WITH TIMEZONE 并尝试存储相同的值,我会得到 ORA-01878
所以我不认为你的时间被转换为 03:00:00 基于 Oracle 试图调整夏令时,我认为你应该收到一个 ORA-01878 当您指定不正确的时间戳时。相反,我认为您的客户端/会话时区与您的数据库/服务器时区不匹配一个小时,而您看到的是正常的 Oracle 调整。
当客户端/会话时区与服务器/数据库时区不匹配时,Oracle 将转换时间戳。由于常规 TIMESTAMP 字段中没有时区信息,Oracle 不介意存储它。
所以您的“静默转换为 2014-10-05 03:00”应该已经发生在其他时间值(您是否尝试插入 01:00 并查看它是否导致调整和/或 ORA-01878 错误?)。
当我在这里尝试将会话时区设置为澳大利亚/悉尼并插入该时间时,我得到:
SQL> alter session set time_zone = 'Australia/Sydney';
Session altered.
SQL> insert into test(ts) values(1, timestamp '2014-10-05 02:00:00');
insert into test values(1, timestamp '2014-10-05 02:00:00')
1 row created.
SQL> insert into test(ts_tz) values(2, timestamp '2014-10-05 02:00:00');
insert into test(ts_tz) values(2, timestamp '2014-10-05 02:00:00')
*
ERROR at line 1:
ORA-01878: specified field not found in datetime or interval
现在,关于您的问题,如果您想存储 UTC 时间,您应该明确指定带有 UTC 的时间戳,或者将您的数据库或会话时区设置为 UTC。您不必拥有 DBA 权限即可设置会话 time_zone,它可以与 db time_zone 不同。
SQL> alter session set time_zone = '+00:00';
Session altered.
SQL> select dbtimezone, sessiontimezone from dual;
DBTIME SESSIONTIMEZONE
------ ---------------------------------------------------------------------------
+00:00 +00:00
SQL> insert into test values(1, timestamp '2014-10-05 02:00:00');
1 row created.
SQL> select * from test;
ID TS
---------- ---------------------------------------------------------------------------
1 05-OCT-14 02.00.00.000000 AM
但是,上述 TIMESTAMP 字段不一定是 UTC。如果数据库服务器时区更改,它可能会更改。对于 Oracle 来说,一个普通的 TIMESTAMP 只是一个没有时区信息的时间戳。如果您想要 UTC 时间,但可能与不同时区的客户打交道,您可以使用 TIMESTAMP WITH TIMEZONE 或 TIMESTAMP WITH LOCAL TIMEZONE。
【讨论】:
感谢您的帮助和详细解答。根据你的笔记,我已经取得了一些进展。我正在使用 Oracle SQL Developer。当数据库中的值为“05/OCT/2014 02:00”时,它在 SQL Dev 中显示为“05/OCT/2014 03:00”,但是使用 TO_CHAR(ts,'HH24') 始终显示 02 小时.我认为这是一个 Java 问题 - 在 SQL Dev 或 JDBC 驱动程序中。我的计算机的时区是澳大利亚/悉尼时区,我相信这会导致 Java 在 SQL Developer 中执行这些静默日期转换。当我将计算机的时区设置为其他时区时,时间会正确显示。 我正在考虑我们只是使用 TIMESTAMP 字段而不是 TIMESTAMP WITH TIME ZONE,原因如下:(1)我们只会存储 UTC,因此时区实际上是多余的,(2 ) 我的印象是 TIMEZONE 字段不受会话时区的影响,因此我们不需要对此进行任何特殊处理,并且 (3) 我怀疑这也意味着 ORA-01878 永远不会发生。希望您有任何想法? 我明天将使用 SQL Developer 进行更多研究。我认为服务器和客户端之间肯定会发生转换。要真正弄清楚这一点,我可能还需要将服务器设置为澳大利亚/悉尼。如果我明天有时间,我会设置我的一个。您是在另一台 PC 上还是直接在服务器上运行 SQL Developer? 是的,SQL Developer 位于与服务器不同的计算机上。非常感谢。 实际上,一个快速的测试就是将日期 2014-10-05 02:00:00 存储在 TIMESTAMP 列中,将您的 PC 更改为悉尼时区,启动 SQL Developer 并查看日期。我猜你会看到同样的行为。以上是关于如何在夏时制开始时跳过的一小时内将时间戳存储在 Oracle 中的主要内容,如果未能解决你的问题,请参考以下文章
Matplotlib:如何在使用日期时间轴绘图时跳过一系列小时?