web-dev-qa-db-ja.com

PostgreSQLデータベースでは、どのタイムスタンプタイプを選択する必要がありますか?

マルチタイムゾーンプロジェクトのコンテキストでPostgresデータベースにタイムスタンプを保存するためのベストプラクティスを定義したいと思います。

私は出来ます

  1. TIMESTAMP WITHOUT TIME ZONEを選択し、このフィールドの挿入時に使用されたタイムゾーンを覚えておいてください
  2. TIMESTAMP WITHOUT TIME ZONEを選択し、挿入時に使用されたタイムゾーンの名前を含む別のフィールドを追加します
  3. TIMESTAMP WITH TIME ZONEを選択し、それに応じてタイムスタンプを挿入します

私はオプション3(タイムゾーン付きタイムスタンプ)を少し好みますが、この問題について知識のある意見が欲しいです。

116
Jerome WAGNER

まず、PostgreSQLの時間処理と算術演算は素晴らしく、一般的な場合はオプション3で十分です。ただし、これは時間とタイムゾーンの不完全なビューであり、補足することができます。

  1. ユーザーのタイムゾーンの名前をユーザー設定として保存します(例:America/Los_Angelesではなく、-0700)。
  2. ユーザーイベント/時間データを参照フレームのローカルに送信します(ほとんどの場合、-0700などのUTCからのオフセット)。
  3. アプリケーションで、時間をUTCに変換し、TIMESTAMP WITH TIME ZONE列を使用して保存します。
  4. ユーザーのタイムゾーンにローカルな時間要求を返します(つまり、UTCからAmerica/Los_Angelesに変換します)。
  5. データベースのtimezoneUTCに設定します。

このオプションは、ユーザーのタイムゾーンを取得するのが難しいため、常に機能するとは限りません。したがって、軽量アプリケーションにTIMESTAMP WITH TIME ZONEを使用するヘッジアドバイスがあります。そうは言っても、このオプション4の背景の側面をいくつか詳しく説明します。

オプション3と同様に、WITH TIME ZONEの理由は、何かが発生した時刻がabsoluteの瞬間であるためです。 WITHOUT TIME ZONEは、relativeタイムゾーンを生成します。絶対タイムスタンプと相対タイムスタンプを混在させないでください。

プログラムと一貫性の観点から、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'はタイムゾーン情報を取り除き、ターゲットの参照フレーム(UTC)を使用して相対TIMESTAMP WITHOUT TIME ZONEを作成します。

不完全な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-6moよりも将来のすべてのスケジュールは相対的なタイムスタンプを使用する必要があります(スケジュール=絶対対計画=相対的 ???)。

相対時間のもう1つの/最後のタイプはINTERVALです。例:「ユーザーがログインしてから20分後にセッションがタイムアウトします」。 INTERVALは、絶対タイムスタンプ(TIMESTAMP WITH TIME ZONE)または相対タイムスタンプ(TIMESTAMP WITHOUT TIME ZONE)で正しく使用できます。 「ログインが成功してから20分後にユーザーセッションの有効期限が切れる(login_utc + session_duration)」または「朝の朝食ミーティングは60分しか続かない(recurring_start_time + meeting_length)」と言うのも同様に正しいです。

混乱の最後のビット:DATETIMETIME 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)

日付とタイムゾーンをデータベースに入れるのは良いことですが、微妙に間違った結果を取得するのは簡単です。時間情報を正しく完全に保存するには、最小限の追加作業が必要です。余分な努力が常に必要であることを意味しません。

139
Sean

ショーンの答えは非常に複雑で誤解を招く。

実際には、「WITH TIME ZONE」と「WITHOUT TIME ZONE」の両方が、Unixのような絶対UTCタイムスタンプとして値を保存します。違いは、タイムスタンプの表示方法にあります。 「タイムゾーン付き」の場合、表示される値は、ユーザーのゾーンに変換されたUTC保存値です。 「タイムゾーンなし」の場合、ユーザーが設定したゾーンに関係なく同じ時計の文字盤を表示するように、UTCに格納された値がねじれます。

「タイムゾーンなし」が使用できる唯一の状況は、実際のゾーンに関係なく時計の文字盤値を適用できる場合です。たとえば、タイムスタンプが投票ブースを閉じるタイミングを示す場合(つまり、人のタイムゾーンに関係なく20:00に閉じる)。

選択肢3を使用します。そうしない特別な理由がない限り、常に「タイムゾーン付き」を使用します。

54
Jay

Postgresはタイムゾーンに関連するタイムスタンプの再計算を行うことができますが、他の2つではそれを自分で行う必要があります。タイムゾーンを使用してタイムスタンプを保存する追加のストレージオーバーヘッドは、何百万ものレコードについて話している場合を除いて、実際には無視できます。

5
GordonM