sql >> データベース >  >> RDS >> PostgreSQL

RailsとPostgreSQLでタイムゾーンを完全に無視する

    Postgresには2つの異なるタイムスタンプデータ型があります:

    • timestamp with time zone 、短縮名: timestamptz
    • timestamp without time zone 、短縮名:timestamp

    timestamptz 推奨です 文字通り、日付/時刻ファミリを入力します。 typispreferredがあります pg_typeで設定 、関連する可能性があります:

    • PostgreSQLで2つの日付間の時系列を生成する

    内部ストレージとエポック

    内部的には、タイムスタンプは8バイトを占めます ディスクとRAMのストレージの。これは、Postgresエポック、2000-01-0100:00:00UTCからのマイクロ秒数を表す整数値です。

    Postgresは、UNIXエポック(1970-01-01 00:00:00 UTC)から秒をカウントする一般的に使用されるUNIX時間に関する知識も組み込んでおり、関数to_timestamp(double precision)でそれを使用します。 またはEXTRACT(EPOCH FROM timestamptz)

    ソースコード:

    * Timestamps, as well as the h/m/s fields of intervals, are stored as
    * int64 values with units of microseconds.  (Once upon a time they were  
    * double values with units of seconds.)
    

    そして:

    /* Julian-date equivalents of Day 0 in Unix and Postgres reckoning */  
    #define UNIX_EPOCH_JDATE        2440588 /* == date2j(1970, 1, 1) */  
    #define POSTGRES_EPOCH_JDATE    2451545 /* == date2j(2000, 1, 1) */  
    

    マイクロ秒の解像度は、1秒間に最大6桁の数字に変換されます。

    timestamp

    timestampの場合 タイムゾーンは明示的に提供されていません。 Postgres無視 誤って入力リテラルにタイムゾーン修飾子が追加されました!

    表示のために時間はシフトされません。すべてが同じタイムゾーンで発生するため、これは問題ありません。別のタイムゾーンの場合、意味 変更されますが、 および表示 変わらない。

    timestamptz

    timestamptzの処理 微妙に異なります。ここでマニュアルを引用します:

    timestamp with time zoneの場合 、内部に保存された値は常にUTCで (協定世界時...)

    大胆な強調鉱山。 タイムゾーン自体は保存されません 。これは、UTCタイムスタンプを計算するために使用される入力修飾子であり、保存されます。または、表示用のローカル時間を計算するために使用される出力デコレータであり、タイムゾーンオフセットが追加されます。 timestamptzのオフセットを追加しない場合 入力時に、セッションの現在のタイムゾーン設定が想定されます。すべての計算はUTCタイムスタンプ値で行われます。複数のタイムゾーンを処理する必要がある場合は、timestamptzを使用してください。 。言い換えると、想定されるタイムゾーンについて疑問や誤解がある場合は、timestamptzを使用してください。 。ほとんどのユースケースに適用されます。

    psqlやpgAdminなどのクライアント、またはlibpqを介して通信するアプリケーション(Rubyとpg gemなど)には、タイムスタンプと現在のタイムゾーンのオフセットが表示されます。 またはリクエストによると タイムゾーン(以下を参照)。常に同じ時点です 、表示形式のみが異なります。または、マニュアルに記載されているように:

    すべてのタイムゾーン対応の日付と時刻は、UTCで内部的に保存されます。 TimeZoneで指定されたゾーンで現地時間に変換されます クライアントに表示される前の構成パラメーター。

    psqlの例:

    db=# SELECT timestamptz '2012-03-05 20:00+03';
          timestamptz
    ------------------------
     2012-03-05 18:00:00+01
    

    ここで何が起こったのですか?
    任意のタイムゾーンオフセット+3を選択しました 入力リテラル用。 Postgresにとって、これはUTCタイムスタンプを入力する多くの方法の1つにすぎません2012-03-05 17:00:00 。クエリの結果は表示されます 現在のタイムゾーン設定Vienna/ Austria 私のテストでは、オフセット+1があります 冬の間および+2 夏時間中(「夏時間」、DST)。したがって、2012-03-05 18:00:00+01 DSTは後で開始するだけです。

    Postgresは入力リテラルをすぐに忘れます。覚えているのは、データ型の値だけです。 10進数と同じように。 numeric '003.4' またはnumeric '+3.4' -どちらもまったく同じ内部値になります。

    AT TIME ZONE

    現在不足しているのは、特定のタイムゾーンに従ってタイムスタンプリテラルを解釈または表現するためのツールです。ここで、 AT TIME ZONE コンストラクトが入ります。2つの異なるユースケースがあります。 timestamptz timestampに変換されます 逆もまた同様です。

    UTC timestamptzを入力するには 2012-03-05 17:00:00+0

    SELECT timestamp '2012-03-05 17:00:00' AT TIME ZONE 'UTC'
    

    ...これは次と同等です:

    SELECT timestamptz '2012-03-05 17:00:00 UTC'
    

    ESTと同じ時点を表示するにはtimestamp (東部標準時):

    SELECT timestamp '2012-03-05 17:00:00' AT TIME ZONE 'UTC' AT TIME ZONE 'EST'
    

    そうです、AT TIME ZONE 'UTC' 2回 。 1つ目は、timestampを解釈します タイプtimestamptzを返す(指定された)UTCタイムスタンプとしての値 。 2番目はtimestamptzを変換します timestampに 指定されたタイムゾーン「EST」-この時点でタイムゾーンESTにウォールクロックが表示する内容。

    SELECT ts AT TIME ZONE 'UTC'
    FROM  (
       VALUES
          (1, timestamptz '2012-03-05 17:00:00+0')
        , (2, timestamptz '2012-03-05 18:00:00+1')
        , (3, timestamptz '2012-03-05 17:00:00 UTC')
        , (4, timestamp   '2012-03-05 11:00:00'  AT TIME ZONE '+6') 
        , (5, timestamp   '2012-03-05 17:00:00'  AT TIME ZONE 'UTC') 
        , (6, timestamp   '2012-03-05 07:00:00'  AT TIME ZONE 'US/Hawaii')  -- ①
        , (7, timestamptz '2012-03-05 07:00:00 US/Hawaii')                  -- ①
        , (8, timestamp   '2012-03-05 07:00:00'  AT TIME ZONE 'HST')        -- ①
        , (9, timestamp   '2012-03-05 18:00:00+1')  -- ② loaded footgun!
          ) t(id, ts);
    

    8(または9)同一を返します 同じUTCタイムスタンプを保持するtimestamptz列を持つ行2012-03-05 17:00:00 。 9行目の種類は私のタイムゾーンで機能しますが、邪悪な罠です。以下を参照してください。

    ①タイムゾーン名前の行6〜8 およびタイムゾーン略語 ハワイの時間はDST(夏時間)の対象であり、現在は異なりますが、異なる場合があります。 'US/Hawaii'のようなタイムゾーン名 HSTのような略語は、DSTルールとすべての履歴シフトを自動的に認識します。 固定オフセットの単なるダムコードです。夏/標準時の別の略語を追加する必要がある場合があります。 名前 すべてを正しく解釈します 指定されたタイムゾーンでのタイムスタンプ。 略語 安価ですが、指定されたタイムスタンプに適したものである必要があります:

    • 同じプロパティのタイムゾーン名をタイムスタンプに適用すると、異なる結果が得られます

    夏時間は、人類がこれまでに思いついた最も優れたアイデアの1つではありません。

    ②行9、ロードされたフットガンとしてマーク 私のために動作します 、しかし偶然によってのみ。リテラルをtimestamp [without time zone]に明示的にキャストした場合 、タイムゾーンオフセットは無視されます!ベアタイムスタンプのみが使用されます。その後、値は自動的にtimestamptzに強制変換されます 例では、列タイプと一致します。このステップでは、timezone 現在のセッションの設定が想定されますが、これはたまたま同じタイムゾーン+1です。 私の場合(ヨーロッパ/ウィーン)。しかし、おそらくあなたの場合はそうではありません-それは異なる値をもたらすでしょう。つまり、timestamptzをキャストしないでください timestampへのリテラル または、タイムゾーンオフセットが失われます。

    あなたの質問

    ユーザーは、たとえば2012年3月17日の午後7時に時間を保存します。タイムゾーン変換またはタイムゾーンを保存したくありません。

    タイムゾーン自体が保存されることはありません。上記のいずれかの方法を使用して、UTCタイムスタンプを入力します。

    ユーザーが指定したタイムゾーンのみを使用して、ユーザーのローカルタイムゾーンの現在時刻の「前」または「後」のレコードを取得します。

    異なるタイムゾーンのすべてのクライアントに対して1つのクエリを使用できます。
    絶対グローバル時間の場合:

    SELECT * FROM tbl WHERE time_col > (now() AT TIME ZONE 'UTC')::time
    

    現地時計による時間:

    SELECT * FROM tbl WHERE time_col > now()::time
    

    まだ背景情報にうんざりしていませんか?マニュアルにはもっとあります。



    1. 月ごとの製品売上を比較するSQLクエリ

    2. 陳記法

    3. MySQL比較データベース

    4. byte []をC#からSQLServerデータベースに保存します