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


119

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

できます

  1. TIMESTAMP WITHOUT TIME ZONEこのフィールドの挿入時に使用されたタイムゾーンを選択して記憶する
  2. TIMESTAMP WITHOUT TIME ZONE挿入時に使用されたタイムゾーンの名前を含む別のフィールドを選択して追加します
  3. TIMESTAMP WITH TIME ZONEタイムスタンプを選択して挿入します

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

回答:


142

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

  1. ユーザーのタイムゾーンの名前をユーザー設定として保存します(などAmerica/Los_Angelesではありません-0700)。
  2. ユーザーイベント/時間データを参照フレームのローカルで送信してもらいます(おそらくUTCからのオフセットなど-0700)。
  3. アプリケーションで、時刻を列に変換しUTCて保存しTIMESTAMP WITH TIME ZONEます。
  4. ユーザーのタイムゾーンにローカルな時間要求を返します(つまりUTCAmerica/Los_Angeles)。
  5. データベースの設定してください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_AngelesUTCからのオフセット(例:-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)」と言っても同じです。

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

日付とタイムゾーンをデータベースに配置することは良いことですが、微妙に不正確な結果を得るの簡単です。 時間情報を正確かつ完全に格納するために最小限の追加作業が必要ですが、それは常に追加の作業が必要であることを意味しません。


2
postgresqlにユーザーのタイムスタンプの正しいタイムゾーンを正確に伝えると、postgresqlはバックグラウンドで大きな作業を行います。自分で変換するのは、面倒なことです。
Seth Robertson、

1
@Sean-チェック制約を使用して、タイムスタンプを挿入せずにどのように挿入しますset timezone to 'UTC'か?あなたはそれを知っているすべてのタイムゾーンを意識した日付はUTCで内部的に保存されていますか

2
チェックのポイントは、データがUTCからのオフセットがゼロで格納されていることを確認することです。情報のソートと取得、およびゼロ以外のオフセットとの時間の比較は、エラーが発生しやすくなります。UTCオフセットをゼロにすることで、すべてのシナリオで予測どおりに動作するほぼゼロのリスクで、単一の観点からデータを一貫して操作できます。タイムスタンプがタイムゾーンのテキスト表現をサポートすることが実用的である場合、このテーマに関する私の考えは異なります。:〜]
Sean

6
@Sean:ただし、ジャックが示すように、すべてのタイムゾーン対応タイムスタンプは基本的にUTCに内部的に保存され、使用時にローカルタイムゾーンに変換されます。事実上、extract(...からのタイムゾーン)は常に、接続のローカルタイムゾーンが何であっても常に返します。タイムスタンプが「格納された」方法とは関係ありません。言い換えると、タイムゾーンはタイプの一部ではなく、格納することもできません。「タイムゾーン付き」は、他のタイプとやり取りするときにデータが変換される方法のプロパティにすぎません。したがって、データには、テキストまたはその他の方法でタイムゾーンがまったく表示されません。
Jay Freeman -saurik- 2012

@ JayFreeman-saurik-:あなたは絶対的に正しいです。'' CHECK() ''は、怪しげなコードから保護するためのフットシュート対策として存在します。データが書き込み時にUTCであることを保証することにより、コードが十分に検討されたか、実行環境が正しく設定されていることが適度に保証されます。
ショーン・

58

ショーンの答えは過度に複雑で誤解を招くものです。

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

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

選択肢3を使用します。特別な理由がない限り、常に「WITH時間帯」を使用してください。


10
Postgresの主要な専門家であるDavid E. Wheelerは、彼の投稿「常にタイムゾーンでタイムスタンプを使用する」に従って評価に同意します。
バジルブルク2014

2
ブラウザでUTCタイムスタンプをローカルタイムゾーンに変換する場合はどうでしょうか。したがって、dbは変換を行わず、UTCのみを含みます。「タイムゾーンなし」は許容されますか?
dman 2016年

5

私の好みはオプション3です。Postgresはタイムゾーンに基づいてタイムスタンプを再計算する作業のほとんどを行うことができますが、他の2つでは自分で行う必要があります。タイムゾーンを使用してタイムスタンプを保存することによる追加のストレージオーバーヘッドは、何百万ものレコードを話している場合を除いて、ごくわずかです。


19
不正解です。オーバーヘッドはありませ …Postgresはタイムゾーンを保存しませ(ところで、「オフセット」は正しい用語であり、タイムゾーンではありません)。TIMESTAMP WITH TIME ZONE名前は誤解を招くです。それは本当に「挿入/更新時に指定されたオフセットに注意を払い、そのオフセットを使用して日時をUTCに調整する」ことを意味します。このTIMESTAMP WITHOUT TIME ZONE名前は、「挿入/更新中に存在する可能性があるオフセットを無視し、日付と時刻の部分を調整の必要がないUTCであると見なす」ことを意味します。ドキュメントを注意深くお読みください。
バジルブルク14

1
@BasilBourqueこの情報をありがとうございます。信じられないほど便利です。これを読んでいる他の人は、ドキュメントの行で次のように述べています。入力値であり、タイムゾーンに合わせて調整されていません。 "
Aidan Rosswood
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.