具有本地时区值透明转换的 Oracle 时间戳
Oracle timestamp with local time zone values transparent translation
据我所知,TIMESTAMP WITH LOCAL TIME ZONE
值是透明的
与用户的会话时区相互转换。但我的价值观来自
数据库与之前插入的不同。是否有数据库或会话
我可以调整参数来解决这个问题吗?
这是我的测试用例:
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_COL
值特别困惑。据我所知,无论当前会话的 TIME_ZONE
是什么,我从 DATE_COL
读取的值应该与我插入的值相同(只要它在我的插入和 [=37= 之间没有改变) ]).
我也对 SYSTIMESTAMP
默认值感到困惑。
SELECT SYSTIMESTAMP FROM DUAL
始终 returns 我的服务器时间戳为“+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
这可能吗?还是我遗漏了什么?
更新。这是我的 LiveSQL script。它看起来应该像我描述的那样工作,所以我猜我的数据库设置可能有问题。
TIMESTAMP WITH LOCAL TIME ZONE
的工作方式如下:当您必须在应用程序中使用时区时,常见的方法是
Store all times internally as UTC and convert them to current user local time zone on application level.
这正是 TIMESTAMP WITH LOCAL TIME ZONE
的工作原理 - 唯一的区别是
Store all times internally as DBTIMEZONE and convert them to current user local time zone on application level.
因此,如果数据库包含 table 和 TIMESTAMP WITH LOCAL TIME ZONE
列并且该列包含数据。
SYSTIMESTAMP
返回数据库服务器操作系统的时区。 DBTIMEZONE
不是 SYSTIMESTAMP
或 SYSDATE
的时区。
DBTIMEZONE
定义了TIMESTAMP WITH LOCAL TIME ZONE
数据类型列的内部存储格式。忘了这个,我无法想象您需要它的任何用例。
实际上你的 table 相当于这个 select:
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 time zone} with local time zone)
时,您尝试将没有任何时区信息的 date/time 值转换为带有时区的 date/time 值。原则上这是不可能的,因为 Oracle 缺少时区信息,因此 Oracle 假设了一个时区。如果您进行此类转换,那么 Oracle 始终会考虑 SESSIONTIMEZONE
中给出的 {time without time zone}(在转换时)。
所以 CAST(sysdate AS timestamp(0) with local time zone)
等同于
CAST(FROM_TZ(TO_TIMESTAMP(SYSDATE), SESSIONTIMEZONE) AS TIMESTAMP(0) WITH LOCAL TIME ZONE)`
分别。 CAST(timestamp '2017-03-15 19:02:00' AS timestamp(0) with local time zone)
表示
CAST(FROM_TZ(TIMESTAMP '2017-03-15 19:02:00', SESSIONTIMEZONE) AS TIMESTAMP(0) WITH LOCAL TIME ZONE)
对于 SYSDATE
这实际上是错误的,因为 SYSDATE
是在数据库服务器操作系统的时区而不是 SESSIONTIMEZONE 中给出的。对于第二个结果是否正确取决于您的意图。
SYSTIMESTAMP
returns值TIMESTAMP WITH TIME ZONE
,它始终独立于你当前的SESSIONTIMEZONE
。但是,如果您转换为 TIMESTAMP WITH LOCAL TIME ZONE
,它当然会转换为您当前的本地时区。您也可以使用 CURRENT_TIMESTAMP
或 SYSTIMESTAMP AT LOCAL
或多或少相同。
此代码
select systimestamp(0) from dual;
-- SYSTIMESTAMP 15/03/2017 19:01:13 +03:00
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
好像是错的。结果应该是
-- 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
差异看起来应该如此,但绝对值似乎是 "faked"(或者您的数据库确实存在问题)。
我们的 DBA 已找到此行为的原因。
我们使用多租户容器数据库 (Oracle 12c CDB)。
当根数据库的 DBTIMEZONE
与可插拔数据库 (PDB) 的 DBTIMEZONE
.
不同时,会出现此问题
在我们的案例中,我们有:
- root
DBTIMEZONE
— “0:00”
- PDB
DBTIMEZONE
— “-7:00”
一旦 DBA 将所有 DBTIMEZONE
设置为相同的值,问题就消失了。
据我了解,他将根 DBTIMEZONE
更改为“-7:00”。
现在我的测试用例运行与 livesql.oracle.com 沙箱完全相同,并且所选时间戳与插入时间相同。
据我所知,TIMESTAMP WITH LOCAL TIME ZONE
值是透明的
与用户的会话时区相互转换。但我的价值观来自
数据库与之前插入的不同。是否有数据库或会话
我可以调整参数来解决这个问题吗?
这是我的测试用例:
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_COL
值特别困惑。据我所知,无论当前会话的 TIME_ZONE
是什么,我从 DATE_COL
读取的值应该与我插入的值相同(只要它在我的插入和 [=37= 之间没有改变) ]).
我也对 SYSTIMESTAMP
默认值感到困惑。
SELECT SYSTIMESTAMP FROM DUAL
始终 returns 我的服务器时间戳为“+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
这可能吗?还是我遗漏了什么?
更新。这是我的 LiveSQL script。它看起来应该像我描述的那样工作,所以我猜我的数据库设置可能有问题。
TIMESTAMP WITH LOCAL TIME ZONE
的工作方式如下:当您必须在应用程序中使用时区时,常见的方法是
Store all times internally as UTC and convert them to current user local time zone on application level.
这正是 TIMESTAMP WITH LOCAL TIME ZONE
的工作原理 - 唯一的区别是
Store all times internally as DBTIMEZONE and convert them to current user local time zone on application level.
因此,如果数据库包含 table 和 TIMESTAMP WITH LOCAL TIME ZONE
列并且该列包含数据。
SYSTIMESTAMP
返回数据库服务器操作系统的时区。 DBTIMEZONE
不是 SYSTIMESTAMP
或 SYSDATE
的时区。
DBTIMEZONE
定义了TIMESTAMP WITH LOCAL TIME ZONE
数据类型列的内部存储格式。忘了这个,我无法想象您需要它的任何用例。
实际上你的 table 相当于这个 select:
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 time zone} with local time zone)
时,您尝试将没有任何时区信息的 date/time 值转换为带有时区的 date/time 值。原则上这是不可能的,因为 Oracle 缺少时区信息,因此 Oracle 假设了一个时区。如果您进行此类转换,那么 Oracle 始终会考虑 SESSIONTIMEZONE
中给出的 {time without time zone}(在转换时)。
所以 CAST(sysdate AS timestamp(0) with local time zone)
等同于
CAST(FROM_TZ(TO_TIMESTAMP(SYSDATE), SESSIONTIMEZONE) AS TIMESTAMP(0) WITH LOCAL TIME ZONE)`
分别。 CAST(timestamp '2017-03-15 19:02:00' AS timestamp(0) with local time zone)
表示
CAST(FROM_TZ(TIMESTAMP '2017-03-15 19:02:00', SESSIONTIMEZONE) AS TIMESTAMP(0) WITH LOCAL TIME ZONE)
对于 SYSDATE
这实际上是错误的,因为 SYSDATE
是在数据库服务器操作系统的时区而不是 SESSIONTIMEZONE 中给出的。对于第二个结果是否正确取决于您的意图。
SYSTIMESTAMP
returns值TIMESTAMP WITH TIME ZONE
,它始终独立于你当前的SESSIONTIMEZONE
。但是,如果您转换为 TIMESTAMP WITH LOCAL TIME ZONE
,它当然会转换为您当前的本地时区。您也可以使用 CURRENT_TIMESTAMP
或 SYSTIMESTAMP AT LOCAL
或多或少相同。
此代码
select systimestamp(0) from dual; -- SYSTIMESTAMP 15/03/2017 19:01:13 +03:00 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
好像是错的。结果应该是
-- 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
差异看起来应该如此,但绝对值似乎是 "faked"(或者您的数据库确实存在问题)。
我们的 DBA 已找到此行为的原因。
我们使用多租户容器数据库 (Oracle 12c CDB)。
当根数据库的 DBTIMEZONE
与可插拔数据库 (PDB) 的 DBTIMEZONE
.
在我们的案例中,我们有:
- root
DBTIMEZONE
— “0:00” - PDB
DBTIMEZONE
— “-7:00”
一旦 DBA 将所有 DBTIMEZONE
设置为相同的值,问题就消失了。
据我了解,他将根 DBTIMEZONE
更改为“-7:00”。
现在我的测试用例运行与 livesql.oracle.com 沙箱完全相同,并且所选时间戳与插入时间相同。