简体   繁体   中英

Converting timestamp field to proper time zone

Our data is stored at the timezone UTC+00:00.

To make some query, we have to convert our timestamp input from 'Europe/Paris' time zone to UTC+00:00 ie Europe/London. And when we get this data, convert the output timestamp to 'Europe/Paris' time zone.

So the solution that I found was to use the "at time zone" like cast(VALIDATIONTIME as timestamp) at time zone 'Europe/Paris' It works with the input but not with the output(when we get data).

So how to make it to work for the input and the output?

Example:

Orders table
ID      VALIDATIONTIME
9986    14/03/16 09:47:29,823000000
6764    20/03/16 12:07:39,453000000

To get the row id 9986, the query would be:

select cast(VALIDATIONTIME as timestamp) at time zone 'Europe/Paris' as validation 
from orders 
where VALIDATIONTIME between cast(TO_TIMESTAMP('14/03/2016 10:45:18', 'DD/MM/YYYY HH24:MI:SS') as timestamp) at time zone 'UTC'
and cast(TO_TIMESTAMP('14/03/2016 10:50:18', 'DD/MM/YYYY HH24:MI:SS') as timestamp) at time zone 'UTC';

But the output (validation) doesn't change!!

The at time zone syntax converts a timestamp with time zone value from it's current zone to the one you specify. When you apply it to a plain timestamp without a timezone, as you are following your cast() , there's an implicit conversion to the session time zone first.

I think you're really looking for the from_tz() function :

select VALIDATIONTIME at time zone 'Europe/Paris' as validation 
from orders 
where VALIDATIONTIME between
  FROM_TZ(TO_TIMESTAMP('14/03/2016 10:45:18', 'DD/MM/YYYY HH24:MI:SS'), 'Europe/Paris')
and
  FROM_TZ(TO_TIMESTAMP('14/03/2016 10:50:18', 'DD/MM/YYYY HH24:MI:SS'), 'Europe/Paris');

You can see the generated time with a standalone query; this also converts the time specified as being in the Paris time zone to UTC for comparison:

select
  FROM_TZ(TO_TIMESTAMP('14/03/2016 10:45:18', 'DD/MM/YYYY HH24:MI:SS'), 'Europe/Paris')
    as Paris,
  FROM_TZ(TO_TIMESTAMP('14/03/2016 10:45:18', 'DD/MM/YYYY HH24:MI:SS'), 'Europe/Paris')
    at time zone 'UTC' as London
from dual;

PARIS                                      LONDON                                   
------------------------------------------ ------------------------------------------
14-MAR-16 10.45.18.000000000 EUROPE/PARIS  14-MAR-16 09.45.18.000000000 UTC          

(Of course, London isn't UTC in the summer, but we'll ignore that detail for now; I'll assume you actually want UTC and not London).

You may have to manipulate validationtime more depending on its data type and the session time zone. With some simple data setup and validationtime as a plain timestamp (with no time zone info):

create table orders (id number, validationtime timestamp);
insert into orders (id, validationtime)
values (9986, to_timestamp('14/03/16 09:47:29,823000000', 'DD/MM/RR HH24:MI:SS,FF'));
insert into orders (id, validationtime)
values (6764, to_timestamp('20/03/16 12:07:39,453000000', 'DD/MM/RR HH24:MI:SS,FF'));

If my session is on London time then the basic query works:

alter session set time_zone = 'Europe/London';

select VALIDATIONTIME at time zone 'Europe/Paris' as validation 
from orders 
where VALIDATIONTIME between
  FROM_TZ(TO_TIMESTAMP('14/03/2016 10:45:18', 'DD/MM/YYYY HH24:MI:SS'), 'Europe/Paris')
and
  FROM_TZ(TO_TIMESTAMP('14/03/2016 10:50:18', 'DD/MM/YYYY HH24:MI:SS'), 'Europe/Paris');

VALIDATION                               
------------------------------------------
14-MAR-16 10.47.29.823000000 EUROPE/PARIS 

But if I changer the session time zone to Paris then now it doesn't:

alter session set time_zone = 'Europe/Paris';

select VALIDATIONTIME at time zone 'Europe/Paris' as validation 
from orders 
where VALIDATIONTIME between
  FROM_TZ(TO_TIMESTAMP('14/03/2016 10:45:18', 'DD/MM/YYYY HH24:MI:SS'), 'Europe/Paris')
and
  FROM_TZ(TO_TIMESTAMP('14/03/2016 10:50:18', 'DD/MM/YYYY HH24:MI:SS'), 'Europe/Paris');

no rows selected

So you need to specify that the original timestamp in your table it UTC, which you can do with the same function:

select FROM_TZ(VALIDATIONTIME, 'UTC') at time zone 'Europe/Paris' as validation 
from orders 
where FROM_TZ(VALIDATIONTIME, 'UTC') between
  FROM_TZ(TO_TIMESTAMP('14/03/2016 10:45:18', 'DD/MM/YYYY HH24:MI:SS'), 'Europe/Paris')
and
  FROM_TZ(TO_TIMESTAMP('14/03/2016 10:50:18', 'DD/MM/YYYY HH24:MI:SS'), 'Europe/Paris');

VALIDATION                               
------------------------------------------
14-MAR-16 10.47.29.823000000 EUROPE/PARIS 

Essentially you need to make sure all the dates you're using are in the same zone before you compare them. Another way is to force the variable dates to UTC and then to plain timestamps:

select FROM_TZ(VALIDATIONTIME, 'UTC') at time zone 'Europe/Paris' as validation 
from orders 
where VALIDATIONTIME
between
  cast(FROM_TZ(TO_TIMESTAMP('14/03/2016 10:45:18', 'DD/MM/YYYY HH24:MI:SS'),
    'Europe/Paris') at time zone 'UTC' as timestamp)
and
  cast(FROM_TZ(TO_TIMESTAMP('14/03/2016 10:50:18', 'DD/MM/YYYY HH24:MI:SS'),
    'Europe/Paris') at time zone 'UTC' as timestamp);

VALIDATION                               
------------------------------------------
14-MAR-16 10.47.29.823000000 EUROPE/PARIS 

... which looks more complicated but as it doesn't modify the table column in the where clause it will allow any index on that column to still be used.

Here the variable string is converted to a plain timestamp, declared as being in the Paris time zone, converted to UTC, and then cast back to a plain timestamp - so it's in the same format and (implied) time zone as the table column data.

Oracle Setup :

CREATE TABLE orders (
  ID INT,
  VALIDATIONTIME TIMESTAMP
);

INSERT INTO orders VALUES (
  9968,
  FROM_TZ( TIMESTAMP '2016-03-14 09:47:29.823', 'Europe/Paris' )
    AT TIME ZONE 'UTC'
);

-- Alternate syntax for inserting from TZ:
INSERT INTO orders VALUES (
  6764,
  TO_TIMESTAMP_TZ( '2016-03-20 20:07:39.453 Europe/Paris',
                   'YYYY-MM-DD HH24:MI:SS.FF TZR' )
    AT TIME ZONE 'UTC'
);

Or even simpler:

INSERT INTO orders VALUES (
  9968,
  TIMESTAMP '2016-03-14 09:47:29.823+01:00' AT TIME ZONE 'UTC'
);

or:

INSERT INTO orders VALUES (
  6764,
  TIMESTAMP '2016-03-20 20:07:39.453 Europe/Paris' AT TIME ZONE 'UTC'
);

Query 1 :

SELECT * FROM orders;

  ID VALIDATIONTIME
---- -----------------------------
9986 2016-03-14 08:47:29.823000000
6764 2016-03-20 19:07:39.453000000

(This shows the UTC time in the table)

Query 2 :

SELECT id,
       CAST( VALIDATIONTIME AS TIMESTAMP WITH TIME ZONE )
         AT TIME ZONE 'Europe/Paris' AS validationtime
FROM   orders

  ID VALIDATIONTIME
---- ------------------------------------------
9986 2016-03-14 09:47:29.823000000 EUROPE/PARIS
6764 2016-03-20 20:07:39.453000000 EUROPE/PARIS

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