oracle db timezone用于会话和数据库,会话区域的更改不起作用

Cas*_*ils 2 database oracle session timezone

我试图让DB以与它在机场相同的方式显示日期.例如,如果您在德克萨斯州并且需要乘坐飞机前往东海岸,机场侯爵将在您当地时间显示起飞时间和着陆时间.因此,例如从达拉斯飞往纽约的航班将显示该地区当地时间的时间.

Marquis in Dallas :     Takeoff time : 8AM  Landing time: 10AM  
Marquis in New York:    Takeoff time : 9AM  Landing time: 11AM
Run Code Online (Sandbox Code Playgroud)

为了做到这一点,我认为数据库会以UTC格式存储时间.我知道TIMESTAMP没有与之关联的区域 - 但是 - 它确实允许人们将时间安排到附加了区域的数据库- 所以 - 我的想法是,已经执行了某种计算以将其转换为世界标准时间.但是,根据我在下面的小测试,这似乎没有发生.无论我将SESSION TIME ZONE设置为什么,日期都保持不变.

TIA

SQL> create table toast ( t timestamp );
Table created.


SQL> insert into toast values ( TIMESTAMP '2019-09-23 16:03:11 US/Eastern');
1 row created.


SQL> select dbtimezone from dual;
DBT
---
UTC

SQL> select sessiontimezone from dual;
SESSIONTIMEZONE
---------------------------------------------------------------------------
-04:00


SQL> select * from toast;
T
---------------------------------------------------------------------------
23-SEP-19 04.03.11.000000 PM
Run Code Online (Sandbox Code Playgroud)

更改会话中的时区仍然会获得相同的日期

SQL> alter session set time_zone = 'America/Chicago';
Session altered.

SQL> select sessiontimezone from dual;
SESSIONTIMEZONE
---------------------------------------------------------------------------
America/Chicago

SQL> select * from toast;
T
---------------------------------------------------------------------------
23-SEP-19 04.03.11.000000 PM
Run Code Online (Sandbox Code Playgroud)

再次改变它,结果相同

SQL> alter session set time_zone = 'Pacific/Auckland';
Session altered.


SQL> select * from toast;
T
---------------------------------------------------------------------------
23-SEP-19 04.03.11.000000 PM
Run Code Online (Sandbox Code Playgroud)

改为使用小时代替,结果相同

SQL> SQL> alter session set time_zone = '-3:00';
Session altered.


SQL> select sessiontimezone from dual;
SESSIONTIMEZONE
---------------------------------------------------------------------------
-03:00


SQL> select * from toast;
T
---------------------------------------------------------------------------
23-SEP-19 04.03.11.000000 PM
Run Code Online (Sandbox Code Playgroud)

更新 非常感谢@Alex Poole的详细回复!

我正在使用Hibernate,Java和Oracle,并且遇到了一些使用Hibernate保存基于时间的数据的问题(关于那部分,我在这里看到这篇文章使用JAVA Calendar类格式化解决方案).文章在这里:如何使用Java中的TimeZone处理Oracle TimeStamp我还看过你之前提到的关于"tirade"的文章(以及其他文章).他们似乎不鼓励使用TIMESTAMP和LOCAL TIMEZONE.只是因为这个信息,我有点想完全坚持使用TIMESTAMP :-)但是,还有TIMESTAMP WITH TIMEZONE的选项.

您对使用此Oracle类型有什么想法吗?

Ale*_*ole 7

您误解了数据类型.时间戳不存储时区,正如你所指出的,但它也确实没有让你"节省时间的DB与它相连的区域".

当你这样做时:

insert into toast values ( TIMESTAMP '2019-09-23 16:03:11 US/Eastern');
Run Code Online (Sandbox Code Playgroud)

你正在将文字值隐式转换为普通时间戳,就好像在做:

insert into toast values ( cast (TIMESTAMP '2019-09-23 16:03:11 US/Eastern' as timestamp ) );
Run Code Online (Sandbox Code Playgroud)

原始区域信息不会保留或可用.没有转换(到UTC或其他任何东西),原始时区信息被丢弃.

select TIMESTAMP '2019-09-23 16:03:11 US/Eastern',
  cast (TIMESTAMP '2019-09-23 16:03:11 US/Eastern' as timestamp )
from dual;

TIMESTAMP'2019-09-2316:03:11US/EASTERN' CAST(TIMESTAMP'2019-09-2316:
--------------------------------------- ----------------------------
23-SEP-19 16.03.11.000000000 US/EASTERN 23-SEP-19 16.03.11.000000000
Run Code Online (Sandbox Code Playgroud)

时间戳文字的原始值显示时区; 演员值没有时区信息.

timestamp正如您所见,更改会话时区对平原没有影响,因为没有时区信息可以产生任何影响.您必须创建数据类型timestamp with time zonetimestamp with local time zone对其产生任何影响.

在您的情况下,因为您最终将处理不同时区中的两个值,仅使用会话时区将无法真正帮助您.您可以存储出发/到达机场的时区感知时间:

create table toast ( depart timestamp with time zone,
  arrive timestamp with time zone);

insert into toast ( depart, arrive )
values ( TIMESTAMP '2019-09-23 08:00:00 US/Central',
  TIMESTAMP '2019-09-23 11:00:00 US/Eastern' );

alter session set time_zone = 'UTC';

Session altered.

select to_char(depart, 'HH24:MI TZR') as depart,
  to_char(arrive, 'HH24:MI TZR') as arrive
from toast;

DEPART                                 ARRIVE                                
-------------------------------------- --------------------------------------
08:00 US/CENTRAL                       11:00 US/EASTERN                      
Run Code Online (Sandbox Code Playgroud)

然后使用datetime表达式调整到本地机场/显示时区,显式地使用命名区域:

select to_char(depart at time zone 'US/Central', 'HH24:MI TZR') as depart,
  to_char(arrive at time zone 'US/Central', 'HH24:MI TZR') as arrive
from toast;

DEPART                                 ARRIVE                                
-------------------------------------- --------------------------------------
08:00 US/CENTRAL                       10:00 US/CENTRAL                      

select to_char(depart at time zone 'US/Eastern', 'HH24:MI TZR') as depart,
  to_char(arrive at time zone 'US/Eastern', 'HH24:MI TZR') as arrive
from toast;

DEPART                                 ARRIVE                                
-------------------------------------- --------------------------------------
09:00 US/EASTERN                       11:00 US/EASTERN                      
Run Code Online (Sandbox Code Playgroud)

或者通过本地会话时区,如果您确信这是正确的:

alter session set time_zone = 'US/Central';

select to_char(depart at local, 'HH24:MI TZR') as depart,
  to_char(arrive at local, 'HH24:MI TZR') as arrive
from toast;

DEPART                                 ARRIVE                                
-------------------------------------- --------------------------------------
08:00 US/CENTRAL                       10:00 US/CENTRAL                      

alter session set time_zone = 'US/Eastern';

select to_char(depart at local, 'HH24:MI TZR') as depart,
  to_char(arrive at local, 'HH24:MI TZR') as arrive
from toast;

DEPART                                 ARRIVE                                
-------------------------------------- --------------------------------------
09:00 US/EASTERN                       11:00 US/EASTERN                      
Run Code Online (Sandbox Code Playgroud)

最好将时间存储为UTC,如果您愿意,可以在普通时间戳中完成 - 所以一切都假定存储的值始终为UTC - 并将您的原始时间转换为UTC,手动或使用sys_extract_utc():

create table toast ( depart timestamp, arrive timestamp);

insert into toast ( depart, arrive )
values ( sys_extract_utc ( TIMESTAMP '2019-09-23 08:00:00 US/Central' ),
  sys_extract_utc ( TIMESTAMP '2019-09-23 11:00:00 US/Eastern' ) );

...

alter session set time_zone = 'US/Eastern';

select to_char(from_tz( depart, 'UTC' ) at local, 'HH24:MI TZR') as depart,
  to_char(from_tz ( arrive, 'UTC' ) at local, 'HH24:MI TZR') as arrive
from toast;

DEPART                                 ARRIVE                                
-------------------------------------- --------------------------------------
09:00 US/EASTERN                       11:00 US/EASTERN                      
Run Code Online (Sandbox Code Playgroud)

但包括该地区可能更安全:

create table toast ( depart timestamp with time zone,
  arrive timestamp with time zone);

insert into toast ( depart, arrive )
values ( TIMESTAMP '2019-09-23 08:00:00 US/Central' at time zone 'UTC',
  TIMESTAMP '2019-09-23 11:00:00 US/Eastern' at time zone 'UTC' );

...

alter session set time_zone = 'US/Eastern';

select to_char(depart at local, 'HH24:MI TZR') as depart,
  to_char(arrive at local, 'HH24:MI TZR') as arrive
from toast;

DEPART                                 ARRIVE                                
-------------------------------------- --------------------------------------
09:00 US/EASTERN                       11:00 US/EASTERN                      
Run Code Online (Sandbox Code Playgroud)

但是,如果你使用它timestamp with local time zone可以更好地兼顾两者,更简单,并且不管你如何转换输入时间:

create table toast ( depart timestamp with local time zone,
  arrive timestamp with local time zone);

insert into toast ( depart, arrive )
values ( TIMESTAMP '2019-09-23 08:00:00 US/Central',
  TIMESTAMP '2019-09-23 11:00:00 US/Eastern' at time zone 'UTC' );

alter session set time_zone = 'UTC';

select to_char(depart, 'HH24:MI TZR') as depart,
  to_char(arrive, 'HH24:MI TZR') as arrive
from toast;

DEPART                                 ARRIVE                                
-------------------------------------- --------------------------------------
13:00 UTC                              15:00 UTC                             

alter session set time_zone = 'US/Central';

select to_char(depart, 'HH24:MI TZR') as depart,
  to_char(arrive, 'HH24:MI TZR') as arrive
from toast;

DEPART                                 ARRIVE                                
-------------------------------------- --------------------------------------
08:00 US/CENTRAL                       10:00 US/CENTRAL                      

alter session set time_zone = 'US/Eastern';

select to_char(depart, 'HH24:MI TZR') as depart,
  to_char(arrive, 'HH24:MI TZR') as arrive
from toast;

DEPART                                 ARRIVE                                
-------------------------------------- --------------------------------------
09:00 US/EASTERN                       11:00 US/EASTERN                      
Run Code Online (Sandbox Code Playgroud)

(另请阅读Tony's Tirade对TIMESTAMP WITH TIME ZONE以获取有关数据类型选项的更多背景知识.)