まず、PostgreSQLの時間処理と演算は素晴らしく、一般的なケースではオプション3で問題ありません。ただし、これは時間とタイムゾーンの不完全なビューであり、補足することができます:
- ユーザーのタイムゾーンの名前をユーザー設定として保存します(例:
America/Los_Angeles
、-0700
ではありません 。 - ユーザーイベント/時間データを参照フレームのローカルに送信します(
-0700
などのUTCからのオフセットである可能性があります) 。 - アプリケーションで、時間を
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時PSTに開始されます。」ローカルタイムゾーンに関係なく、イベントが発生した場所にテレポートできれば、イベントの発生を目撃できます。データベース内のほとんどの時間データは絶対値です(したがって、TIMESTAMP WITH TIME ZONE
である必要があります 、理想的には+0オフセットと、オフセットではなく特定のタイムゾーンを管理するルールを表すテキストラベルを使用します。
相対的なイベントは、まだ決定されていないタイムゾーンの観点から何かの時間を記録またはスケジュールすることです。例:「私たちのビジネスのドアは午前8時に開き、午後9時に閉じます」、「毎週月曜日の午前7時に会議を行い、毎週の朝食会を開きます」、「毎週ハロウィーンの午後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計画=相対???)。
他の/最後のタイプの相対時間は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
深夜を識別するために使用できるタイムゾーン情報がないため、相対的な日付を表します。同様に、'23:23:59'::TIME
タイムゾーンもDATE
もわからないため、相対的です 時間で表されます。 '23:59:59-07'::TIME WITH TIME ZONE
でも 、DATE
がわからない だろう。そして最後に、DATE
タイムゾーンのあるものは実際には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)
日付とタイムゾーンをデータベースに入れるのは良いことですが、微妙に間違った結果を得るのは簡単です。 時間情報を正確かつ完全に保存するために必要な追加の作業は最小限ですが、それは常に追加の作業が必要であることを意味するわけではありません。