带有本地时区值的Oracle时间戳透明转换
据我所知,带有本地时区值的Oracle时间戳透明转换,oracle,session,timezone,timestamp-with-timezone,Oracle,Session,Timezone,Timestamp With Timezone,据我所知,带有本地时区的时间戳值是透明的 转换到用户会话时区或从用户会话时区转换。但我的价值观是从 数据库与以前插入的数据库不同。有数据库或会话吗 我可以调整参数来解决这个问题吗 以下是我的测试用例: select systimestamp(0) from dual; -- SYSTIMESTAMP 15/03/2017 19:01:13 +03:00 select dbtimezone from dual; -- DBTIMEZONE -07:00 create table
带有本地时区的时间戳
值是透明的
转换到用户会话时区或从用户会话时区转换。但我的价值观是从
数据库与以前插入的数据库不同。有数据库或会话吗
我可以调整参数来解决这个问题吗
以下是我的测试用例:
select systimestamp(0) from dual;
-- SYSTIMESTAMP 15/03/2017 19:01:13 +03:00
select dbtimezone from dual;
-- DBTIMEZONE -07:00
create table test_timestamps
(
id number generated by default on null as identity,
systimestamp_col timestamp(0) with local time zone default on null systimestamp,
sysdate_col timestamp(0) with local time zone default on null sysdate,
current_timestamp_col timestamp(0) with local time zone default on null current_timestamp(0),
date_col timestamp(0) with local time zone
);
alter session set time_zone='0:00';
insert into test_timestamps(date_col)
values (timestamp '2017-03-15 19:02:00');
select * from test_timestamps;
-- ID 1
-- SYSTIMESTAMP_COL 15/03/2017 9:02:19
-- SYSDATE_COL 15/03/2017 12:02:18
-- CURRENT_TIMESTAMP_COL 15/03/2017 9:02:19
-- DATE_COL 15/03/2017 12:02:00
delete from test_timestamps;
alter session set time_zone='+3:00';
insert into test_timestamps(date_col)
values (timestamp '2017-03-15 19:05:00');
select * from test_timestamps;
-- ID 2
-- SYSTIMESTAMP_COL 15/03/2017 12:05:43
-- SYSDATE_COL 15/03/2017 12:05:43
-- CURRENT_TIMESTAMP_COL 15/03/2017 12:05:43
-- DATE_COL 15/03/2017 12:05:00
我对DATE\u COL
值特别困惑。据我所知,无论当前会话的时区是什么,我从DATE\u COL
读取的值都应该与我插入的值相同(只要在插入和选择之间没有更改)
我还对SYSTIMESTAMP
默认值感到困惑
SELECT SYSTIMESTAMP FROM DUAL
始终返回带有“+3:00”时区的服务器时间戳,而不管当前用户会话的时区如何。但是如果我使用SYSTIMESTAMP
作为列的默认值,它会被转换
我想看到的是:
-- for a user in my time zone
alter session set time_zone='+3:00';
insert into test_timestamps(date_col)
values (timestamp '2017-03-15 19:15:00');
select id, systimestamp_col, date_col from test_timestamps;
-- ID 3
-- SYSTIMESTAMP_COL 15/03/2017 19:15:00
-- DATE_COL 15/03/2017 19:15:00
-- same data from a GMT user's point of view
alter session set time_zone='+0:00';
select id, systimestamp_col, date_col from test_timestamps;
-- ID 3
-- SYSTIMESTAMP_COL 15/03/2017 16:15:00
-- DATE_COL 15/03/2017 16:15:00
这是可能的还是我遗漏了什么
UPD。这是我的名片。它看起来应该像我描述的那样工作,所以我猜我的数据库设置可能有问题。带有本地时区的时间戳
工作原理如下:当您必须在应用程序中使用时区时,通常的方法是
将所有时间在内部存储为UTC,并在应用程序级别将其转换为当前用户本地时区
这正是本地时区的时间戳的工作原理-唯一的区别是
在内部将所有时间存储为DBTIMEZONE,并在应用程序级别将其转换为当前用户本地时区
因此,如果数据库包含带有带有本地时区
列的时间戳的表,并且该列包含数据,则不能再更改数据库上的DBTIMEZONE
(使用ALTER DATABASE SET TIME_ZONE='…';
)
SYSTIMESTAMP
在数据库服务器操作系统的时区中返回DBTIMEZONE
不是SYSTIMESTAMP
或SYSDATE
的时区
DBTIMEZONE
定义带有本地时区的时间戳的内部存储格式
数据类型列。忘了这一点吧,我无法想象您需要它的任何用例
实际上,您的表相当于此选择:
select
CAST(systimestamp AS timestamp(0) with local time zone) as SYSTIMESTAMP_COL,
CAST(sysdate AS timestamp(0) with local time zone) as SYSDATE_COL,
CAST(current_timestamp AS timestamp(0) with local time zone) as CURRENT_TIMESTAMP_COL,
CAST(timestamp '2017-03-15 19:02:00' AS timestamp(0) with local time zone) as DATE_COL
from dual;
当您进行CAST({time without timezone}with local timezone)
时,您将尝试将不带任何时区信息的日期/时间值转换为带时区的日期/时间值。原则上这是不可能的,因为Oracle缺少时区信息,所以Oracle假定为时区。如果您进行这样的转换,Oracle将始终考虑{time without timezone},如SESSIONTIMEZONE
中所示(在转换时)
因此CAST(sysdate作为时间戳(0)和本地时区)
相当于
CAST(FROM_TZ(TO_TIMESTAMP(SYSDATE), SESSIONTIMEZONE) AS TIMESTAMP(0) WITH LOCAL TIME ZONE)`
分别<代码>强制转换(时间戳“2017-03-15 19:02:00”为带有本地时区的时间戳(0)
表示
CAST(FROM_TZ(TIMESTAMP '2017-03-15 19:02:00', SESSIONTIMEZONE) AS TIMESTAMP(0) WITH LOCAL TIME ZONE)
对于SYSDATE
这实际上是错误的,因为SYSDATE
是在数据库服务器操作系统的时区中给出的,而不是在SESSIONTIMEZONE中。对于第二个,这取决于你的意图,结果是否正确
SYSTIMESTAMP
返回带时区的值时间戳
,它始终独立于当前的会话时区
。但是,如果您转换为带有本地时区的时间戳,它当然会转换为您当前的本地时区。您还可以使用CURRENT\u TIMESTAMP
或SYSTIMESTAMP AT LOCAL
,其作用大致相同
此代码
似乎是错的。结果应该是
-- SYSTIMESTAMP_COL 15/03/2017 16:01:14
-- SYSDATE_COL 15/03/2017 19:01:14
-- CURRENT_TIMESTAMP_COL 15/03/2017 16:01:14
-- DATE_COL 15/03/2017 19:02:00
差异看起来是应该的,但绝对值似乎是“伪造的”(或者您的数据库存在实际问题)。我们的DBA已经找到了这种行为的原因。 我们使用一个多租户容器数据库(Oracle 12c CDB)。 当根数据库的
DBTIMEZONE
与可插拔数据库(PDB)DBTIMEZONE
不同时,会出现此问题
就我们而言,我们有:
- root
-“0:00”DBTIMEZONE
- PDB
-“-7:00”DBTIMEZONE
DBTIMEZONE
s设置为相同的值,问题就消失了。
据我所知,他已将根DBTIMEZONE
更改为“-7:00”。
现在,我的测试用例与沙箱上的运行完全相同,所选时间戳与插入的时间戳相同。感谢您的回答!我包括了
DBTIMEZONE
,因为我的SELECT
返回的值关闭了10个小时,看起来像+7+3(我的服务器的时区加上DBTIMEZONE值)。尽管如此,我仍然不知道如何在我的数据库实例上对时间戳值进行简单的往返。如果我在我的DATE\u COL
列中插入时间戳文字(甚至转换为带有本地时区的timestamp
),select将返回偏移10小时的值。在其他Oracle实例上,例如在livesql.Oracle.com上,它的行为与此不同,这就是我怀疑某些配置问题的原因。当我的会话时区设置为0:00
时,我插入19:00
,然后返回9:00
,这将关闭10小时。当我的会话时区为+3:00
(与数据库服务器的机器相同)时,我会得到12:00
,这会被关闭7小时。也许这是巧合,DBTIMEZONE
是-7:00
,但我怀疑这可能是相关的,这就是为什么我认为值得一提的原因。谢谢Wernfried,非常感谢您的帮助!这些值不是伪造的,我按原样发布了所有内容。我无法在新创建的Oracle数据库上重现这个问题,这就是为什么我如此困惑的原因。幸运的是,我们的DBA刚刚找到了这种行为的原因,我将
-- SYSTIMESTAMP_COL 15/03/2017 16:01:14
-- SYSDATE_COL 15/03/2017 19:01:14
-- CURRENT_TIMESTAMP_COL 15/03/2017 16:01:14
-- DATE_COL 15/03/2017 19:02:00