レガシー システムからデータを取得して表示するモバイル アプリを開発しています。レガシー システムでは、日時列は TIMESTAMP WITH TIME ZONE ではなく Date データ型に格納されます。次に、モバイル アプリでタイム ゾーンの問題を処理する必要があります。
データを表示するユーザーに代わってデータを適切に解釈できるように、データを入力したユーザーのタイム ゾーン (GMT からのオフセット) をデータの時刻に反映する (または少なくとも GMT で保存する) 必要があります。
私たちの「極端なケース」の状況は、タイム ゾーン A のレガシー システム ユーザーがタイム ゾーン A の注文を入力し、データベース サーバーがタイム ゾーン B に、モバイル アプリ ユーザーがタイム ゾーン C にいる場合です。
データにはオフセット情報がないように見え、GMT ではないため、各ステップでタイム ゾーンが「推測」されます。
タイム ゾーン A のユーザーには、そのタイム ゾーンで入力された時刻から「正しい」時刻が表示されるため、正しく解釈されます。
モバイル アプリのデータ サービス (データセットを永続化/シリアル化する) は、タイム ゾーン B でデータベース サーバーからデータを読み取り、時間をタイム ゾーン B にあると解釈し、時間は数時間ずれています。
タイム ゾーン C のモバイル アプリ ユーザーは、時刻がタイム ゾーン C にあると解釈し、時刻が別の時間数ずれています。
入力された注文のタイム ゾーン情報があれば、それを表示する方法を決定できます。
「発信者」のタイムゾーンに関して
「視聴者」のタイムゾーンに関して
GMT で絶対時間があれば、「視聴者」のタイム ゾーンで表示できます (「発信者」のタイム ゾーンはわかりません)。
従来の DATE データ型を TIMESTAMP WITH TIME ZONE に変更することは選択肢ではありません。これを行うには多大な労力がかかり、他の問題が発生する可能性があるためです。
これが私のバンドエイドソリューションです
UTC 情報を含むロケーション テーブルを作成します。
loc_code 列を FK として orders テーブルに追加します。
注文とロケーション テーブルを結合し、order_date を TIMESTAMP WITH TIME ZONE に変換します。
このソリューションには穴があることを知っており、目標を達成するためのより良い方法を探しています。任意の入力をいただければ幸いです。
以下はコードです、
DROP TABLE location;
CREATE TABLE location
(
loc_code VARCHAR2(6) NOT NULL,
descr VARCHAR2(40) NOT NULL,
utc_time_zone_offset VARCHAR2(6) NULL,
daylight_savings_start_date DATE NULL,
daylight_savings_end_date DATE NULL,
CONSTRAINT locationp1 PRIMARY KEY (loc_code)
);
INSERT INTO location VALUES ('-5','EST','-05.00',To_Date('2013-03-10 2:00:00','yyyy-mm-dd hh24:mi:ss'),To_Date('2013-11-03 2:00:00','yyyy-mm-dd hh24:mi:ss'));
INSERT INTO location VALUES ('-6','CST','-06.00',To_Date('2013-03-10 2:00:00','yyyy-mm-dd hh24:mi:ss'),To_Date('2013-11-03 2:00:00','yyyy-mm-dd hh24:mi:ss'));
INSERT INTO location VALUES ('-7','MST','-07.00',To_Date('2013-03-10 2:00:00','yyyy-mm-dd hh24:mi:ss'),To_Date('2013-11-03 2:00:00','yyyy-mm-dd hh24:mi:ss'));
INSERT INTO location VALUES ('-8','PST','-08.00',To_Date('2013-03-10 2:00:00','yyyy-mm-dd hh24:mi:ss'),To_Date('2013-11-03 2:00:00','yyyy-mm-dd hh24:mi:ss'));
INSERT INTO location VALUES ('-9','ALST','-09.00',To_Date('2013-03-10 2:00:00','yyyy-mm-dd hh24:mi:ss'),To_Date('2013-11-03 2:00:00','yyyy-mm-dd hh24:mi:ss'));
INSERT INTO location VALUES ('-10','HST','-10.00',To_Date('2013-03-10 2:00:00','yyyy-mm-dd hh24:mi:ss'),To_Date('2013-11-03 2:00:00','yyyy-mm-dd hh24:mi:ss'));
COMMIT;
DROP TABLE orders
CREATE TABLE orders (order_id VARCHAR2(10),loc_code VARCHAR2(6), order_date DATE);
SELECT order_id,order_date,
cs_timezone.to_UCT(loc_code,order_date),
cs_timezone.to_viewer_time(loc_code,order_date)
FROM orders
CREATE OR REPLACE PACKAGE cs_timezone AUTHID CURRENT_USER AS
-- 1. Only SYSDATE is affected by SYSTIMESTAMP which the timestamp on the server machine itself.
-- If we do not use SYSDATE, we do not have to worry about SYSTIMESTAMP.
-- 2. When insert a DATE value Oracle only stores the date time value without any knowledge of time zone.
-- When we convert to local time we only care about time zone offset for the user who save the data.
FUNCTION to_viewer_time (p_loc_code IN VARCHAR2, p_date IN DATE) RETURN DATE;
FUNCTION to_UCT (p_loc_code IN VARCHAR2, p_date IN DATE) RETURN TIMESTAMP WITH TIME ZONE;
END;
/
CREATE OR REPLACE PACKAGE BODY cs_timezone
AS
FUNCTION to_viewer_time (p_loc_code IN VARCHAR2, p_date IN DATE) RETURN DATE
IS
v_offset_hrs INT; v_utc_time_zone_offset VARCHAR2(10); v_daylight_savings_start_date DATE; v_daylight_savings_end_date DATE;
BEGIN
SELECT utc_time_zone_offset, daylight_savings_start_date, daylight_savings_end_date
INTO v_utc_time_zone_offset, v_daylight_savings_start_date, v_daylight_savings_end_date
FROM location
WHERE loc_code = p_loc_code;
IF InStr(v_utc_time_zone_offset,'+') > 0 THEN
IF To_Date(To_Char(p_date,'mm-dd hh24:mi:ss'),'mm-dd hh24:mi:ss') BETWEEN To_Date(To_Char(v_daylight_savings_start_date,'mm-dd hh24:mi:ss'),'mm-dd hh24:mi:ss') AND To_Date(To_Char(v_daylight_savings_end_date,'mm-dd hh24:mi:ss'),'mm-dd hh24:mi:ss') THEN
v_utc_time_zone_offset := SubStr(v_utc_time_zone_offset,1,1)||To_Char(To_Number(v_utc_time_zone_offset)+1);
END IF;
v_offset_hrs := extract(TIMEZONE_HOUR FROM current_timestamp) - To_Number(v_utc_time_zone_offset);
ELSIF InStr(v_utc_time_zone_offset,'-') > 0 THEN
IF To_Date(To_Char(p_date,'mm-dd hh24:mi:ss'),'mm-dd hh24:mi:ss') BETWEEN To_Date(To_Char(v_daylight_savings_start_date,'mm-dd hh24:mi:ss'),'mm-dd hh24:mi:ss') AND To_Date(To_Char(v_daylight_savings_end_date,'mm-dd hh24:mi:ss'),'mm-dd hh24:mi:ss') THEN
v_utc_time_zone_offset := To_Char(To_Number(v_utc_time_zone_offset)+1);
END IF;
v_offset_hrs := To_Number(v_utc_time_zone_offset) - extract(TIMEZONE_HOUR FROM current_timestamp);
ELSE
Raise_Application_Error(-20001,'Offset format missing - or +');
END IF;
Dbms_Output.put_line(v_offset_hrs);
RETURN p_date+(v_offset_hrs/24);
EXCEPTION WHEN OTHERS THEN RETURN 'ERROR: '||SQLERRM;
END to_viewer_time;
FUNCTION to_UCT (p_loc_code IN VARCHAR2, p_date IN DATE) RETURN TIMESTAMP WITH TIME ZONE
IS
v_utc_time_zone_offset VARCHAR2(10); v_daylight_savings_start_date DATE; v_daylight_savings_end_date DATE;
BEGIN
SELECT utc_time_zone_offset, daylight_savings_start_date, daylight_savings_end_date
INTO v_utc_time_zone_offset, v_daylight_savings_start_date, v_daylight_savings_end_date
FROM location
WHERE loc_code = p_loc_code;
IF InStr(v_utc_time_zone_offset,'+') > 0 THEN
IF To_Date(To_Char(p_date,'mm-dd hh24:mi:ss'),'mm-dd hh24:mi:ss') BETWEEN To_Date(To_Char(v_daylight_savings_start_date,'mm-dd hh24:mi:ss'),'mm-dd hh24:mi:ss') AND To_Date(To_Char(v_daylight_savings_end_date,'mm-dd hh24:mi:ss'),'mm-dd hh24:mi:ss') THEN
v_utc_time_zone_offset := SubStr(v_utc_time_zone_offset,1,1)||To_Char(To_Number(v_utc_time_zone_offset)+1);
IF InStr(v_utc_time_zone_offset,'-') = 0 OR InStr(v_utc_time_zone_offset,'+') = 0 THEN
v_utc_time_zone_offset := '+'||v_utc_time_zone_offset;
END IF;
IF InStr(v_utc_time_zone_offset,'.00') = 0 THEN
v_utc_time_zone_offset := v_utc_time_zone_offset||':00';
ELSE
v_utc_time_zone_offset := REPLACE(v_utc_time_zone_offset,'.',':');
END IF;
ELSE
v_utc_time_zone_offset := REPLACE(v_utc_time_zone_offset,'.',':');
END IF;
ELSIF InStr(v_utc_time_zone_offset,'-') > 0 THEN
IF To_Date(To_Char(p_date,'mm-dd hh24:mi:ss'),'mm-dd hh24:mi:ss') BETWEEN To_Date(To_Char(v_daylight_savings_start_date,'mm-dd hh24:mi:ss'),'mm-dd hh24:mi:ss') AND To_Date(To_Char(v_daylight_savings_end_date,'mm-dd hh24:mi:ss'),'mm-dd hh24:mi:ss') THEN
v_utc_time_zone_offset := To_Char(To_Number(v_utc_time_zone_offset)+1);
IF InStr(v_utc_time_zone_offset,'-') = 0 OR InStr(v_utc_time_zone_offset,'+') = 0 THEN
v_utc_time_zone_offset := '+'||v_utc_time_zone_offset;
END IF;
IF InStr(v_utc_time_zone_offset,'.00') = 0 THEN
v_utc_time_zone_offset := v_utc_time_zone_offset||':00';
ELSE
v_utc_time_zone_offset := REPLACE(v_utc_time_zone_offset,'.',':');
END IF;
ELSE
v_utc_time_zone_offset := REPLACE(v_utc_time_zone_offset,'.',':');
END IF;
ELSE
Raise_Application_Error(-20001,'Offset format missing - or +');
END IF;
Dbms_Output.put_line('');
RETURN TO_TIMESTAMP_TZ(To_Char(p_date,'YYYY-MM-DD HH24:MI:SS')||' '||v_utc_time_zone_offset, 'YYYY-MM-DD HH24:MI:SS TZH:TZM');
EXCEPTION WHEN OTHERS THEN RETURN 'ERROR: '||SQLERRM;
END to_UCT;
END cs_timezone;
ありがとう、
ショーン