【问题标题】:oracle db timezone for session and database, change of session zone not working会话和数据库的 oracle db 时区,会话区域的更改不起作用
【发布时间】:2017-08-01 11:04:01
【问题描述】:

我正在尝试让数据库以与在机场相同的方式显示日期。例如,如果您在德克萨斯州,并且需要搭乘航班飞往东海岸,机场侯爵会显示起飞时间和降落时间您的当地时间。因此,例如从达拉斯飞往纽约的航班将显示该地区的当地时间。

Marquis in Dallas :     Takeoff time : 8AM  Landing time: 10AM  
Marquis in New York:    Takeoff time : 9AM  Landing time: 11AM

为了做到这一点,我认为数据库会以 UTC 格式存储时间。我知道 TIMESTAMP 没有与之关联的区域 - 但是 - 它确实允许人们将时间保存到带有区域的数据库 附加到它 - 所以 - 我的想法是某种计算会已执行将其转换为 UTC。但是,根据我下面的小测试,这似乎并没有发生。无论我将 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

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

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

再改一次,结果一样

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


SQL> select * from toast;
T
---------------------------------------------------------------------------
23-SEP-19 04.03.11.000000 PM

改为使用小时来更改它,得到相同的结果

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

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

我正在使用 Hibernate、Java 和 Oracle,并且在使用 Hibernate 保存基于时间的数据时遇到了一些问题(关于那部分,我在这里看到了这篇文章,它使用 JAVA 日历类格式化了一个解决方案)。文章在这里:How To Handle Oracle TimeStamp with TimeZone from Java 我也看过你之前提到的关于“长篇大论”的论文(以及其他文章)。他们似乎不鼓励使用 TIMESTAMP WITH LOCAL TIMEZONE。只是因为这个信息,我有点试图完全坚持使用 TIMESTAMP :-) 但是,也有 TIMESTAMP WITH TIMEZONE 的选项。

你对这个Oracle类型的使用有什么想法吗?

【问题讨论】:

    标签: database oracle session timezone


    【解决方案1】:

    您误解了数据类型。正如您所指出的,时间戳不存储时区,但它也确实允许您“将时间保存到附加区域的数据库中”。

    当你这样做时:

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

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

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

    原始区域信息不保留或不可用。没有转换(到 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
    

    时间戳文字的原始值显示时区;转换值没有时区信息。

    正如您所见,更改会话时区对普通的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                      
    

    然后调整到当地机场/显示时区with datetime expressions,或者明确地使用命名区域:

    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                      
    

    如果您确信这是正确的,也可以通过本地会话时区:

    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                      
    

    不过,将时间存储为 UTC 可能会更好,如果您愿意,也可以在纯时间戳中完成 - 所以一切都假定存储的值始终为 UTC - 并将您的原始时间手动或使用 @ 转换为 UTC 987654325@:

    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                      
    

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

    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                      
    

    但是,如果您使用 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                      
    

    (另请阅读 Tony’s Tirade against TIMESTAMP WITH TIME ZONE 了解有关数据类型选项的更多背景信息。)

    【讨论】:

    • @Alex Poole 非常感谢您的详细回答!我用以下问题更新了味精:我看过你之前提到的论文。我也看过其他文章。基本上,似乎不鼓励使用带有本地时区的时间戳(?)如果是这样,那么带有时区的时间戳是否与带有本地时区的时间戳一样好? TIA
    • @CaseyHarrils - 您对 Tony 文章中表格下的要点的解释一定与我非常不同; “当您在不同时区复制数据时,请使用 TIMESTAMP WITH LOCAL TIME ZONE”和“不要打扰 TIMESTAMP 或 TIMESTAMP WITH TIME ZONE”。所有这些都存在问题,但timestamp with local time zone 似乎是最简单的并且(基于 Don Tonay 的工作)最适合您正在做的事情?如上所示,坚持使用timestamp 并将所有内容强制为 UTC 也是可行的,并且可能是航空业的常用方法。
    • @Alex Poole LOL - 是的,我误读了这篇文章 - 这篇文章是为了使用 TIMESTAMP WITH LOCAL TIMEZONE 而这里的文章:palashray.com/… 似乎反对它:-|不过,两者都是很久以前写的。无论哪种方式,TIMESTAMP WITH LOCAL TIMEZONE 最适合我的情况。再次感谢您的所有意见!
    【解决方案2】:

    试试这个:

    create table toast ( t timestamp WITH LOCAL TIME ZONE );
    

    类似于 TIMESTAMP WITH TIME ZONE,只是数据是 存储时标准化为数据库时区,并调整为 检索时匹配客户端的时区。

    【讨论】:

      猜你喜欢
      • 2019-01-04
      • 2017-12-21
      • 1970-01-01
      • 2013-08-16
      • 1970-01-01
      • 2011-02-17
      • 1970-01-01
      • 1970-01-01
      • 2013-04-05
      相关资源
      最近更新 更多