简体   繁体   中英

oracle db timezone for session and database, change of session zone not working

I am trying to get the DB to display dates in the same way it works at airports. For example, if you are in Texas and you need to take a flight to the East coast, the airport marquis will display the takeoff time and landing time in your local time . So, for example a flight from Dallas to New York would show the times in the local time for the area.

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

In order to do this, I thought the DB would store the times in UTC. I know that TIMESTAMP does not have a Zone associated with it - but - it does allow one to save time to a DB with the Zone attached to it - so - my thoughts were that some kind of calculation would have been performed to convert it to UTC. But, according to my little test below, this does not seem to be going on. The date stays the same no matter what I set the SESSION TIME ZONE to.

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

changing the timezone in the session still gets the same date

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

changing it again, same results

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


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

changed it using hours instead, got same result

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

Update Thanks so much @Alex Poole for that detailed response!

I was working with Hibernate, Java and Oracle and was having some issues saving time-based data using Hibernate (regarding that part, I saw this post here that formats a solution using the JAVA Calendar class). The article is here: How To Handle Oracle TimeStamp with TimeZone from Java I had also glanced at the paper you mentioned about the "tirade" before (and other articles as well). They seemed to discourage the use of TIMESTAMP WITH LOCAL TIMEZONE. It was only because of this info that I was kinda trying to stick with TIMESTAMP altogether :-) But, there is also the option of TIMESTAMP WITH TIMEZONE.

Do you have any thoughts on the use of this Oracle type?

You are misunderstanding the data type. A timestamp does not store a time zone, as you pointed out, but it also does not allow you to "save time to a DB with the Zone attached to it".

When you do:

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

you are doing an implicit conversion of the literal value to a plain timestamp, as if doing:

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

The original zone information is not retained or available. There is no conversion (to UTC or anything else), the original time zone information is just discarded.

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

The original value from your timestamp literal shows the timezone; the cast value has no time zone information.

Changing the session time zone has no effect on a plain timestamp , as you've seen, as there is no time zone information to have any effect on. You would have to make the data type timestamp with time zone or timestamp with local time zone for it to have any impact.

In your case as you will ultimately be dealing with two values in different time zones, only using the session time zone won't really help you. You could store the time zone-aware times for the departure/arrival airports:

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                      

and then adjust to the local airport/display time zone with datetime expressions , either explicitly with a named region:

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                      

or via the local session time zone if you're confident that is correct:

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                      

It may be better to store the times as UTC though, which can be done in a plain timestamp still if you prefer - so everything assumes the stored value is always UTC - and convert your original times to UTC, manually or with 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                      

But including the region is maybe safer:

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                      

But if you use timestamp with local time zone you kind of get the best of both, more simply, and regarless of how you convert the input time:

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                      

(Also read Tony's Tirade against TIMESTAMP WITH TIME ZONE for some more background on the data type options.)

Try this:

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

Similar to TIMESTAMP WITH TIME ZONE, except that the data is normalized to the database time zone when stored, and adjusted to match the client's time zone when retrieved.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM