如何在Oracle中存储时间戳,该时间戳在夏令时开始时跳过的小时内发生

时间:2014-07-14 09:00:43

标签: sql oracle timestamp dst

我们的Oracle服务器在澳大利亚/悉尼时间运行。

我们计划以UTC格式存储一些新日期。

例如,我们可能存储的日期之一是 2014年10月5日凌晨2:00

然而,在悉尼,我们同时开始夏令时,这意味着当天不存在从凌晨2:00 凌晨2:59 的时间。< / p>

例如,在 2014年10月5日上,发生的时间是:

1点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 |
+---+---------------------------------+--------------------------------------------------+

1 个答案:

答案 0 :(得分:0)

  
    

麻烦的是,如果我尝试将时间2014-10-05 02:00存储在数据库中,它会默默转换为2014-10-05 03:00

  

在我的数据库中,我可以将会话时区设置为“澳大利亚/悉尼”,并且仍然在一个普通的TIMESTAMP列中存储2014-10-05 02:00:00而不进行转换。但如果我将其更改为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。如果DB服务器时区发生更改,它可能会更改。对于Oracle,简单的TIMESTAMP只是一个没有时区信息的时间戳。如果您想要UTC时间,但可能正在处理不同时区的客户端,您可以使用TIMESTAMP WITH TIMEZONE或TIMESTAMP WITH LOCAL TIMEZONE。