「+ hh:mm」(または「-hh:mm」)の形式の文字列を使用することを考えています。これは必要十分ですか?
注:日付や時刻を保存する必要はなく、タイムゾーンだけを保存します。
残念ながら、PostgreSQLはタイムゾーンデータ型を提供していないため、おそらくtext
を使用する必要があります。
interval
は一見論理的なオプションのように見えますが、is一部の用途に適しています。ただし、夏時間を考慮していません。また、同じUTCオフセット内の異なる地域が異なるDSTルールを持っているという事実も考慮していません。
UTCオフセットからタイムゾーンへの1:1マッピングはありません。
たとえば、Australia/Sydney
(ニューサウスウェールズ州)のタイムゾーンは、夏時間中のUTC+10
(EST
)またはUTC+11
(EDT
)です。 。はい、それは米国が使用しているのと同じ頭字語EST
です。タイムゾーンの頭字語はtzdataデータベースでは一意ではありません。そのため、Pgにはtimezone_abbreviations
設定があります。さらに悪いことに、ブリスベン(クイーンズランド州)はほぼ同じ長さでUTC+10 EST
にありますが、夏時間がないため、NSWのDST中にニューサウスウェールズに-1
オフセットすることがあります。
(更新:最近オーストラリアはA
プレフィックスを採用したため、東部の州のTZ頭字語としてAEST
を使用しますが、EST
とWST
は引き続き一般的に使用されます)。
混乱しますか?
保存する必要があるのがTCオフセットだけの場合は、interval
が適切です。 タイムゾーンを保存する場合は、text
として保存します。現時点で検証してタイムゾーンオフセットに変換するのは面倒ですが、少なくともDSTに対応しています。
「+ hh:mm」と「-hh:mm」はタイムゾーンではなく、UTCオフセットです。それらを保存するのに適した形式は、分単位のオフセットを持つ符号付き整数です。 interval
のようなものを使用することもできますが、クエリなどのようにPostgreSQLで直接日付計算を実行する場合にのみ役立ちます。通常、これらの計算は別の言語で実行しますが、状況によって異なります。その言語でinterval
タイプを適切にサポートし、適切な日付/時刻ライブラリがあるかどうか。しかし、整数をPython interval
のような、ある種のtimedelta
のような型に変換するのは簡単なはずなので、個人的には整数として格納するだけです。
タイムゾーンには名前があり、タイムゾーンの標準化された名前はありませんが、「tz」または「zoneinfo」データベースには事実上の標準が1つあり、「Europe/Paris」、「Americas/New_York」、「米国/太平洋」。それらは文字列として保存する必要があります。
Windowsは、「ロマンス時間」など、まったく異なる名前を使用します(尋ねないでください)。文字列と同様にそれらを保存できますが、私はそれを避けます。これらの名前はWindowsの外部では使用されず、名前は意味がありません。さらに、Windowsの翻訳バージョンは、これらのタイムゾーンに翻訳された名前を使用する傾向があり、さらに悪化します。
「PDT」や「EST」などの略語は、一意ではないため、タイムゾーン名として使用できません。すべて「CST」と呼ばれる4つの異なるタイムゾーンがあるので(私は思うか、それとも5つでしたか?)、それは使用できません。
要するに:タイムゾーンの場合、名前を文字列として保存します。UTCオフセットの場合、オフセットを分単位で符号付き整数として保存します。
理想的な世界では、既知のタイムゾーンのセットへの外部キーを持つことができます。ビューとドメインを使用して、これに近いことを行うことができます。
これ wikiのヒント David E. Wheleerは、タイムゾーンとしての有効性をテストするドメインを作成します。
CREATE OR REPLACE FUNCTION is_timezone( tz TEXT ) RETURNS BOOLEAN as $$
BEGIN
PERFORM now() AT TIME ZONE tz;
RETURN TRUE;
EXCEPTION WHEN invalid_parameter_value THEN
RETURN FALSE;
END;
$$ language plpgsql STABLE;
CREATE DOMAIN timezone AS CITEXT
CHECK ( is_timezone( value ) );
既知のタイムゾーンのリストがあると便利です。その場合、ドメインを省略して、既知のタイムゾーン名を含む1つのテーブルに制約を適用できます(ビューから取得 pg_timezone_names
)、ドメインを他の場所に公開する必要性を回避します。
CREATE TABLE tzone
(
tzone_name text PRIMARY KEY (tzone_name) CHECK (is_timezone(tzone_name))
);
INSERT INTO tzone (tzone_name)
SELECT name FROM pg_timezone_names;
次に、外部キーを介して正確性を強制できます。
CREATE TABLE myTable (
...
tzone TEXT REFERENCES tzone(tzone_name)
);
Postgresでは、任意のTIMESTAMP
またはTIMESTAMPTZ
を名前付きタイムゾーンとの間でキャストできるため、テーブルから値を検索する必要はありません。この式はチェック制約で直接使用できるため、このための関数を作成する必要もありません。
CREATE TABLE locations (
location_id SERIAL PRIMARY KEY,
name TEXT,
timezone TEXT NOT NULL CHECK (now() AT TIME ZONE timezone IS NOT NULL)
);
有効なタイムゾーンを含まない値を挿入しようとすると、実際にはかなりユーザーフレンドリーなエラーが発生します。
INSERT INTO locations (name, timezone) VALUES ('foo', 'Adelaide/Australia');
ERROR: time zone "Adelaide/Australia" not recognized
要件によっては、needエラーが通常の制約違反で提供される形式である場合がありますが、多くの場合、これで十分です。
ドロップダウンボックスに表示できるタイムゾーンのリストを提供するWebフレームワークを使用している場合は、この検証で十分であり、チェック制約は単なるバックアップです。
多分間隔
postgres =#select interval '01:30 '; interval ---------- 01:30:00 (1行) postgres =#select interval'-01:30 '; interval ----------- -01:30:00 (1行)