web-dev-qa-db-ja.com

タイムゾーンを保存するための適切なデータ型は何ですか?

「+ hh:mm」(または「-hh:mm」)の形式の文字列を使用することを考えています。これは必要十分ですか?

注:日付や時刻を保存する必要はなく、タイムゾーンだけを保存します。

22
jl6

残念ながら、PostgreSQLはタイムゾーンデータ型を提供していないため、おそらくtextを使用する必要があります。

intervalは一見論理的なオプションのように見えますが、is一部の用途に適しています。ただし、夏時間を考慮していません。また、同じUTCオフセット内の異なる地域が異なるDSTルールを持っているという事実も考慮していません。

UTCオフセットからタイムゾーンへの1:1マッピングはありません。

たとえば、Australia/Sydney(ニューサウスウェールズ州)のタイムゾーンは、夏時間中のUTC+10EST)またはUTC+11EDT)です。 。はい、それは米国が使用しているのと同じ頭字語ESTです。タイムゾーンの頭字語はtzdataデータベースでは一意ではありません。そのため、Pgにはtimezone_abbreviations設定があります。さらに悪いことに、ブリスベン(クイーンズランド州)はほぼ同じ長さでUTC+10 ESTにありますが、夏時間がないため、NSWのDST中にニューサウスウェールズに-1オフセットすることがあります。

更新:最近オーストラリアはAプレフィックスを採用したため、東部の州のTZ頭字語としてAESTを使用しますが、ESTWSTは引き続き一般的に使用されます)。

混乱しますか?

保存する必要があるのがTCオフセットだけの場合は、intervalが適切です。 タイムゾーンを保存する場合は、textとして保存します。現時点で検証してタイムゾーンオフセットに変換するのは面倒ですが、少なくともDSTに対応しています。

28
Craig Ringer

「+ 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オフセットの場合、オフセットを分単位で符号付き整数として保存します。

14
Lennart Regebro

理想的な世界では、既知のタイムゾーンのセットへの外部キーを持つことができます。ビューとドメインを使用して、これに近いことを行うことができます。

これ 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)
);
13
beldaz

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フレームワークを使用している場合は、この検証で十分であり、チェック制約は単なるバックアップです。

4

多分間隔

 postgres =#select interval '01:30 '; 
 interval 
 ---------- 
 01:30:00 
(1行)
 
 postgres =#select interval'-01:30 '; 
 interval 
 ----------- 
-01:30:00 
(1行)
0
Pavel Stehule