まず、PostgreSQLの時間処理と演算は素晴らしく、一般的なケースではオプション3で十分です。ただし、これは時間とタイムゾーンの不完全なビューであり、補足することができます。
- ユーザーのタイムゾーンの名前をユーザー設定として保存します(など
America/Los_Angeles
ではありません-0700
)。
- ユーザーイベント/時間データを参照フレームのローカルで送信してもらいます(おそらくUTCからのオフセットなど
-0700
)。
- アプリケーションで、時刻を列に変換し
UTC
て保存しTIMESTAMP WITH TIME ZONE
ます。
- ユーザーのタイムゾーンにローカルな時間要求を返します(つまり
UTC
、America/Los_Angeles
)。
- データベースの設定してください
timezone
にをUTC
。
このオプションは、ユーザーのタイムゾーンを取得することが難しくTIMESTAMP WITH TIME ZONE
、軽量アプリケーションに使用するためのヘッジアドバイスが得られない場合があるため、常に機能するとは限りません。とはいえ、このオプション4のいくつかの背景的な側面について詳しく説明します。
オプション3と同様に、この理由は、WITH TIME ZONE
何かが発生した時間が絶対的な瞬間であるためです。相対タイムゾーンをWITHOUT TIME ZONE
生成します。絶対的なタイムスタンプと相対的なタイムスタンプを混在させないでください。
プログラムと一貫性の観点から、すべての計算がUTCをタイムゾーンとして使用されていることを確認してください。これはPostgreSQLの要件ではありませんが、他のプログラミング言語や環境と統合するときに役立ちます。CHECK
列にを設定して、タイムスタンプ列への書き込みにタイムゾーンオフセット0
があることを確認すると、いくつかのクラスのバグ(スクリプトがデータをファイルにダンプするなど)を使用して時間データをソートする防御的な位置になります。語彙ソート)。繰り返しになりますが、PostgreSQLは日付計算を正しく行うため、またはタイムゾーン間で変換するためにこれを必要としません(つまり、PostgreSQLは2つの任意のタイムゾーン間で時刻を変換することに長けています)。データベースに入るデータがゼロのオフセットで保存されるようにするには:
CREATE TABLE my_tbl (
my_timestamp TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(),
CHECK(EXTRACT(TIMEZONE FROM my_timestamp) = '0')
);
test=> SET timezone = 'America/Los_Angeles';
SET
test=> INSERT INTO my_tbl (my_timestamp) VALUES (NOW());
ERROR: new row for relation "my_tbl" violates check constraint "my_tbl_my_timestamp_check"
test=> SET timezone = 'UTC';
SET
test=> INSERT INTO my_tbl (my_timestamp) VALUES (NOW());
INSERT 0 1
100%完璧ではありませんが、データがすでにUTCに変換されていることを確認するのに十分な強力な足音対策を提供します。これを行う方法については多くの意見がありますが、これは私の経験から実際には最良のようです。
データベースのタイムゾーン処理の批判は大部分が正当化されます(これを非常に無能に処理するデータベースはたくさんあります)が、PostgreSQLのタイムスタンプとタイムゾーンの処理はかなり素晴らしいです(あちこちにいくつかの「機能」があるにもかかわらず)。たとえば、そのような機能の1つ:
-- Make sure we're all working off of the same local time zone
test=> SET timezone = 'America/Los_Angeles';
SET
test=> SELECT NOW();
now
-------------------------------
2011-05-27 15:47:58.138995-07
(1 row)
test=> SELECT NOW() AT TIME ZONE 'UTC';
timezone
----------------------------
2011-05-27 22:48:02.235541
(1 row)
はAT TIME ZONE 'UTC'
タイムゾーン情報を取り除き、TIMESTAMP WITHOUT TIME ZONE
ターゲットの参照フレーム(UTC
)を使用して相対を作成することに注意してください。
不完全なものTIMESTAMP WITHOUT TIME ZONE
からに変換する場合TIMESTAMP WITH TIME ZONE
、不足しているタイムゾーンは接続から継承されます。
test=> SET timezone = 'America/Los_Angeles';
SET
test=> SELECT EXTRACT(TIMEZONE_HOUR FROM NOW());
date_part
-----------
-7
(1 row)
test=> SELECT EXTRACT(TIMEZONE_HOUR FROM TIMESTAMP WITH TIME ZONE '2011-05-27 22:48:02.235541');
date_part
-----------
-7
(1 row)
-- Now change to UTC
test=> SET timezone = 'UTC';
SET
-- Create an absolute time with timezone offset:
test=> SELECT NOW();
now
-------------------------------
2011-05-27 22:48:40.540119+00
(1 row)
-- Creates a relative time in a given frame of reference (i.e. no offset)
test=> SELECT NOW() AT TIME ZONE 'UTC';
timezone
----------------------------
2011-05-27 22:48:49.444446
(1 row)
test=> SELECT EXTRACT(TIMEZONE_HOUR FROM NOW());
date_part
-----------
0
(1 row)
test=> SELECT EXTRACT(TIMEZONE_HOUR FROM TIMESTAMP WITH TIME ZONE '2011-05-27 22:48:02.235541');
date_part
-----------
0
(1 row)
要点:
- ユーザーのタイムゾーンを名前付きラベル(例:)として保存し、
America/Los_Angeles
UTCからのオフセット(例:-0700
)
- ゼロ以外のオフセットを格納するやむを得ない理由がない限り、すべてにUTCを使用する
- ゼロ以外のすべてのUTC時間を入力エラーとして扱う
- 相対タイムスタンプと絶対タイムスタンプを混ぜて一致させることはありません
- 可能であればデータベース
UTC
としても使用timezone
ランダムプログラミング言語に関する注意:Pythonのdatetime
データ型は、絶対時間と相対時間の区別を維持するのに非常に優れています(ただし、最初はPyTZのようなライブラリを追加するまでイライラします)。
編集
相対と絶対の違いについてもう少し説明しましょう。
イベントの記録には絶対時間が使用されます。例:「ユーザー123がログインしました」または「卒業式は2011年5月28日午後2時に開始されます。」ローカルタイムゾーンに関係なく、イベントが発生した場所にテレポートできれば、イベントの発生を目撃できます。データベース内のほとんどの時間データは絶対です(したがってTIMESTAMP WITH TIME ZONE
、理想的には+0オフセットと、オフセットではなく特定のタイムゾーンを管理するルールを表すテキストラベルが必要です)。
相対的なイベントは、まだ決定されていないタイムゾーンの観点から何かの時間を記録またはスケジュールすることです。例:「私たちの会社のドアは午前8時に開き、午後9時に閉じます」、「毎週月曜日の午前7時に週1回の朝食ミーティングに会いましょう」、または「ハロウィーンごとに午後8時」。一般に、相対時間はイベントのテンプレートまたはファクトリで使用され、絶対時間は他のほとんどすべてに使用されます。相対時間の値を示す必要がある、指摘する価値のある1つのまれな例外があります。何かが発生する可能性のある絶対時間について不確実性がある可能性がある将来的に十分遠い将来のイベントの場合、相対タイムスタンプを使用します。これが実際の例です:
それが2004年で、2008年10月31日の米国西海岸の午後1時に配達をスケジュールする必要があるとします(例:America/Los_Angeles
/ PST8PDT
)。を使用して絶対時間を使用してそれを保存した場合’2008-10-31 21:00:00.000000+00’::TIMESTAMP WITH TIME ZONE
、米国政府が夏時間を管理するルールを変更した2005年のエネルギー政策法を可決したため、配達は午後2時に表示されます。配達が予定されていた2004年には、日付10-31-2008
は太平洋標準時(+8000
)でしたが、2005年以降、タイムゾーンデータベース10-31-2008
は太平洋夏時間(+0700
)。相対タイムスタンプはタイムゾーンと一緒に保存すると、正しい配信スケジュールが得られます。これは、相対タイムスタンプが議会の不正な改ざんの影響を受けないためです。相対的な時間と絶対的な時間を使用して物事をスケジュールする際の限界は曖昧な線ですが、私の経験則では、3か月から6か月以上先のスケジュールでは、相対タイムスタンプを使用する必要があります(スケジュール=絶対vs計画=相対的 ???)。
もう1つの/最後のタイプの相対時間はINTERVAL
です。例:「ユーザーがログインしてから20分後にセッションがタイムアウトします」。INTERVAL
絶対タイムスタンプ(のいずれかで正しく使用することができるTIMESTAMP WITH TIME ZONE
)、または相対タイムスタンプ(TIMESTAMP WITHOUT TIME ZONE
)。同様に、「ユーザーセッションはログイン成功後20分で終了します(login_utc + session_duration)」、または「朝の朝食ミーティングは60分間しか継続できません(recurring_start_time + meeting_length)」と言っても同じです。
最後の混乱のビット:DATE
、TIME
、TIME WITHOUT TIME ZONE
およびTIME WITH TIME ZONE
すべての相対的なデータ型です。例:'2011-05-28'::DATE
午前0時の識別に使用できるタイムゾーン情報がないため、相対日付を表します。同様に、'23:23:59'::TIME
タイムゾーンも時間でDATE
表されるものもわからないため、相対的です。を使用しても'23:59:59-07'::TIME WITH TIME ZONE
、どうなるかわかりませんDATE
。そして最後にDATE
、タイムゾーンは実際にはa DATE
ではなく、TIMESTAMP WITH TIME ZONE
:
test=> SET timezone = 'America/Los_Angeles';
SET
test=> SELECT '2011-05-11'::DATE AT TIME ZONE 'UTC';
timezone
---------------------
2011-05-11 07:00:00
(1 row)
test=> SET timezone = 'UTC';
SET
test=> SELECT '2011-05-11'::DATE AT TIME ZONE 'UTC';
timezone
---------------------
2011-05-11 00:00:00
(1 row)
日付とタイムゾーンをデータベースに配置することは良いことですが、微妙に不正確な結果を得るのは簡単です。 時間情報を正確かつ完全に格納するために最小限の追加作業が必要ですが、それは常に追加の作業が必要であることを意味しません。