2016-05-19 15 views
3

質問

original_datedateとする。 SYS_EXTRACT_UTC(cast(original_date as timestamp))の結果から、内部のOracle関数によってのみ、つまりデータベースのタイムゾーン設定に関係なく、その値を戻すにはどうすればよいですか。OracleのSYS_EXTRACT_UTC()の逆は何ですか?

背景

一部愚かプログラムは、それが

cast(SYS_EXTRACT_UTC(SYSTIMESTAMP) as date) 

で、UTCのテーブルのいずれかに日付の値を書き込むには、このテーブルの日付型の列に代わりSYSDATEの格納されています。

他のすべての表では、単にSYSDATEがそのような列に格納されます。私の仕事は、これらの値を一緒に使用することです、私はSYS_EXTRACT_UTC()関数の効果を取り戻したいと思います。私は、時間を節約し、夏の日照がDBTIMEZONE

によって無視されるためか、

cast(FROM_TZ(cast(my_utc_date as TIMESTAMP), 'UTC') AT TIME ZONE 'Europe/Budapest' as date) 

によって、すなわち、しかし、私はDBTIMEZONEの代わり'Europe/Budapest'を、使用している場合、私は間違った結果を取得し、私は手動で自分のタイムゾーンを指定する場合にのみ、この問題を解決することができます例えば

、場合TO_CHAR(SYSDATE、 'YYYY-MM-DD HH24:MI:SS')= '2016年5月19日夜01時45分12秒'、プログラム格納

cast(SYS_EXTRACT_UTC(cast(SYSDATE as timestamp)) as date) 

私のテスト問合せです:

SELECT 
     original_date, 
     stored_utc_date, 
     cast(FROM_TZ(cast(stored_utc_date as TIMESTAMP), 'UTC') AT TIME ZONE 'Europe/Budapest' as date) as reverted_good, 
     cast(FROM_TZ(cast(stored_utc_date as TIMESTAMP), 'UTC') AT TIME ZONE DBTIMEZONE as date) as reverted_wrong 
from (
     select original_date, cast(SYS_EXTRACT_UTC(cast(original_date as timestamp)) as date) stored_utc_date 
     from (select to_date('2016-05-19 13:45:12','YYYY-MM-DD HH24:MI:SS') original_date from dual) 
    ) 

とその結果は次のとおりです。

ORIGINAL_DATE  STORED_UTC_DATE  REVERTED_GOOD  REVERTED_WRONG  
------------------- ------------------- ------------------- ------------------- 
2016-05-19 13:45:12 2016-05-19 11:45:12 2016-05-19 13:45:12 2016-05-19 12:45:12 
+0

あなたは 'DBTIMEZONE'ではなく' SESSIONTIMEZONE'を試しましたか? –

+0

@MartinSchapendonk - これは、クエリを実行するセッションが常にデータベースサーバーと同じタイムゾーンにあるか、少なくともそれが宣言していると仮定します。おそらく安全な仮定ではないでしょう。 –

+0

@AlexPooleこれは安全な仮定ではない場合、レコードが多くの異なるタイムゾーン(sys_extract_utcが抽出にセッションタイムゾーンを使用するため)から発生した可能性があるため、元のタイムスタンプを再構築することは不可能だと思います。 –

答えて

2

あなたはSYSTIMESTAMPタイムゾーン領域を取得し、それを使用できます。テストデータで

FROM_TZ(cast(stored_utc_date as TIMESTAMP), 'UTC') AT TIME ZONE to_char(systimestamp, 'TZR') 

(それは私だとしてロンドンにブダペストを変更地方地域):

SELECT 
     original_date, 
     stored_utc_date, 
     cast(FROM_TZ(cast(stored_utc_date as TIMESTAMP), 'UTC') AT TIME ZONE 'Europe/London' as date) as reverted_good, 
     cast(FROM_TZ(cast(stored_utc_date as TIMESTAMP), 'UTC') AT TIME ZONE DBTIMEZONE as date) as reverted_wrong, 
     cast(FROM_TZ(cast(stored_utc_date as TIMESTAMP), 'UTC') AT TIME ZONE TO_CHAR(systimestamp, 'TZR') as date) as reverted_right 
from (
     select original_date, cast(SYS_EXTRACT_UTC(cast(original_date as timestamp)) as date) stored_utc_date 
     from (select to_date('2016-05-19 13:45:12','YYYY-MM-DD HH24:MI:SS') original_date from dual) 
    ) 
/

ORIGINAL_DATE  STORED_UTC_DATE  REVERTED_GOOD  REVERTED_WRONG  REVERTED_RIGHT  
------------------- ------------------- ------------------- ------------------- ------------------- 
2016-05-19 13:45:12 2016-05-19 12:45:12 2016-05-19 13:45:12 2016-05-19 12:45:12 2016-05-19 13:45:12 

例外を除いて常に動作しませんTZRはオフセット(オペレーティングシステムTZに基づいているため)として報告され、オフセットから領域を推測することはできません。元の日付が冬になっていて、これを夏に実行した場合、またはその逆の場合は、復帰日は1時間になります。半分の復帰日は常に間違っていますが、半分はクエリ実行時に依存します。

ローカルタイムゾーンとしてDBTIMEZONEを使用することによってこの問題を回避することができますように見えます:

cast(FROM_TZ(cast(stored_utc_date as TIMESTAMP), 'UTC') AT TIME ZONE DBTIMEZONE 
    as timestamp with local time zone 

あなたのテストクエリ再び:

SELECT 
     original_date, 
     stored_utc_date, 
     cast(FROM_TZ(cast(stored_utc_date as TIMESTAMP), 'UTC') AT TIME ZONE 'Europe/London' as date) as reverted_good, 
     cast(FROM_TZ(cast(stored_utc_date as TIMESTAMP), 'UTC') AT TIME ZONE DBTIMEZONE as date) as reverted_wrong, 
     cast(cast(FROM_TZ(cast(stored_utc_date as TIMESTAMP), 'UTC') AT TIME ZONE DBTIMEZONE as timestamp with local time zone) as date) as reverted_right 
from (
     select original_date, cast(SYS_EXTRACT_UTC(cast(original_date as timestamp)) as date) stored_utc_date 
     from (select to_date('2016-05-19 13:45:12','YYYY-MM-DD HH24:MI:SS') original_date from dual) 
    ) 
/

ORIGINAL_DATE  STORED_UTC_DATE  REVERTED_GOOD  REVERTED_WRONG  REVERTED_RIGHT  
------------------- ------------------- ------------------- ------------------- ------------------- 
2016-05-19 13:45:12 2016-05-19 12:45:12 2016-05-19 13:45:12 2016-05-19 12:45:12 2016-05-19 13:45:12 

年全体で日付が広いテストクエリ:

with t as (
    select from_tz(cast(add_months(trunc(sysdate, 'MM'), 1-level) as timestamp), 'Europe/London') 
    as original_systimestamp 
    from dual 
    connect by level <= 12 
) 
select original_systimestamp, 
    cast(cast(from_tz(sys_extract_utc(original_systimestamp), 'UTC') 
    at time zone dbtimezone as timestamp with local time zone) as date) as good_date, 
    sys_extract_utc(original_systimestamp) as utc_timestamp, 
    from_tz(sys_extract_utc(original_systimestamp), 'UTC') 
    at time zone to_char(systimestamp, 'TZR') as at_systimezone, 
    from_tz(sys_extract_utc(original_systimestamp), 'UTC') 
    at time zone dbtimezone as at_dbtimezone, 
    cast(from_tz(sys_extract_utc(original_systimestamp), 'UTC') 
    at time zone dbtimezone as timestamp with local time zone) as at_local_dbtimezone 
from t 
order by original_systimestamp; 

ORIGINAL_SYSTIMESTAMP    GOOD_DATE   UTC_TIMESTAMP   AT_SYSTIMEZONE    AT_DBTIMEZONE    AT_LOCAL_DBTIMEZONE   
----------------------------------- ------------------- --------------------- ---------------------------- ---------------------------- ---------------------------- 
2015-06-01 00:00:00.0 Europe/London 2015-06-01 00:00:00 2015-05-31 23:00:00.0 2015-06-01 00:00:00.0 +01:00 2015-05-31 23:00:00.0 +00:00 2015-06-01 00:00:00.0  
2015-07-01 00:00:00.0 Europe/London 2015-07-01 00:00:00 2015-06-30 23:00:00.0 2015-07-01 00:00:00.0 +01:00 2015-06-30 23:00:00.0 +00:00 2015-07-01 00:00:00.0  
2015-08-01 00:00:00.0 Europe/London 2015-08-01 00:00:00 2015-07-31 23:00:00.0 2015-08-01 00:00:00.0 +01:00 2015-07-31 23:00:00.0 +00:00 2015-08-01 00:00:00.0  
2015-09-01 00:00:00.0 Europe/London 2015-09-01 00:00:00 2015-08-31 23:00:00.0 2015-09-01 00:00:00.0 +01:00 2015-08-31 23:00:00.0 +00:00 2015-09-01 00:00:00.0  
2015-10-01 00:00:00.0 Europe/London 2015-10-01 00:00:00 2015-09-30 23:00:00.0 2015-10-01 00:00:00.0 +01:00 2015-09-30 23:00:00.0 +00:00 2015-10-01 00:00:00.0  
2015-11-01 00:00:00.0 Europe/London 2015-11-01 00:00:00 2015-11-01 00:00:00.0 2015-11-01 01:00:00.0 +01:00 2015-11-01 00:00:00.0 +00:00 2015-11-01 00:00:00.0  
2015-12-01 00:00:00.0 Europe/London 2015-12-01 00:00:00 2015-12-01 00:00:00.0 2015-12-01 01:00:00.0 +01:00 2015-12-01 00:00:00.0 +00:00 2015-12-01 00:00:00.0  
2016-01-01 00:00:00.0 Europe/London 2016-01-01 00:00:00 2016-01-01 00:00:00.0 2016-01-01 01:00:00.0 +01:00 2016-01-01 00:00:00.0 +00:00 2016-01-01 00:00:00.0  
2016-02-01 00:00:00.0 Europe/London 2016-02-01 00:00:00 2016-02-01 00:00:00.0 2016-02-01 01:00:00.0 +01:00 2016-02-01 00:00:00.0 +00:00 2016-02-01 00:00:00.0  
2016-03-01 00:00:00.0 Europe/London 2016-03-01 00:00:00 2016-03-01 00:00:00.0 2016-03-01 01:00:00.0 +01:00 2016-03-01 00:00:00.0 +00:00 2016-03-01 00:00:00.0  
2016-04-01 00:00:00.0 Europe/London 2016-04-01 00:00:00 2016-03-31 23:00:00.0 2016-04-01 00:00:00.0 +01:00 2016-03-31 23:00:00.0 +00:00 2016-04-01 00:00:00.0  
2016-05-01 00:00:00.0 Europe/London 2016-05-01 00:00:00 2016-04-30 23:00:00.0 2016-05-01 00:00:00.0 +01:00 2016-04-30 23:00:00.0 +00:00 2016-05-01 00:00:00.0  

でも、セッションタイムゾーンがデータベースサーバーのre gion;私がセッションタイムゾーンをヨーロッパ/ロンドン以外の何かに設定すると、それは途方もないものになります。そして、あなたはセッションタイムゾーンを設定できることに頼っています。問題の最初のクエリは、実際にはそれほど悪くはありません。

DBTIMEZONEはそうではありません必ず有用なことを教えてください。 Oracle recommend setting it to UTC。あなたがそれを使用できない場合、systimestampから抽出されたTZRを実際に使用することはできません(実際にはオフセットであり、それを地域に変換できないのでDST情報を知ることはできません)。データベースに保持されているものから元のシステム時間を把握する方法があると考えてください。セッションタイムゾーンを設定するか、元のクエリと同じように、サーバーのタイムゾーン領域を指定する必要があるようです。

+0

いいえ、これは正しくありません... 'to_char(systimestamp、 'TZR')'は私の場合は+01:00のオフセットとして '地域'を表示するので、結果はそれが夏時間。うーん。 –

+0

パニックはないと思います。今は夏ですから、私は+02:00を見せてくれます。冬には+01:00と表示され、これは私が必要とするものです。 – mma

+0

@ mma - 夏期か冬期かにかかわらず、保存された日付ごとに+01:00または+02:00が適用されます。復帰した日付の半分は1時間です。夏には元の冬の日付が間違っている、冬に元の夏の日付は間違っています。 –

関連する問題