あなたは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情報を知ることはできません)。データベースに保持されているものから元のシステム時間を把握する方法があると考えてください。セッションタイムゾーンを設定するか、元のクエリと同じように、サーバーのタイムゾーン領域を指定する必要があるようです。
あなたは 'DBTIMEZONE'ではなく' SESSIONTIMEZONE'を試しましたか? –
@MartinSchapendonk - これは、クエリを実行するセッションが常にデータベースサーバーと同じタイムゾーンにあるか、少なくともそれが宣言していると仮定します。おそらく安全な仮定ではないでしょう。 –
@AlexPooleこれは安全な仮定ではない場合、レコードが多くの異なるタイムゾーン(sys_extract_utcが抽出にセッションタイムゾーンを使用するため)から発生した可能性があるため、元のタイムスタンプを再構築することは不可能だと思います。 –