web-dev-qa-db-ja.com

タイムスタンプを異なるタイムゾーンと比較するにはどうすればよいですか?

たとえば、タイムスタンプを含むテーブルがあります。

    2018-04-05 06:00:00 +01
    2018-04-05 06:00:00 +00
    2018-04-05 06:00:00 -01
    2018-04-05 04:00:00 -01

そして、私は...であるエントリを取得したいです.

BETWEEN 2018-04-05 06:00:00+01 AND 2018-04-05 07:00:00+01

だから私の出力は:

    2018-04-05 06:00:00 +01
    2018-04-05 04:00:00 -01

演算子BETWEENはタイムゾーンの違いを自動的に処理し、必要な出力を提供しますか?

4
7cart project

これはうまくいきます:

SELECT *, ts AT TIME ZONE '-01'  -- see below about '-1' vs '+1'
FROM  (
   VALUES 
   (1, timestamptz '2018-04-05 06:00:00 +01')
 , (2,             '2018-04-05 06:00:00 +00')
 , (3,             '2018-04-05 06:00:00 -01')
 , (4,             '2018-04-05 04:00:00 -01')
   ) t(id, ts)
WHERE  ts BETWEEN '2018-04-05 06:00:00+01'  -- coerced to timestamptz
              AND '2018-04-05 07:00:00+01'; -- derived from context!

しかし、ここに注意いくつかの落とし穴と潜在的な誤解があります!

  • SQL構文BETWEEN ... AND (関数ではなく、厳密に言えば、実際には演算子のようです)はタイムゾーンに関係しません。それは物事がどのように機能するかについての誤解でしょう。

  • 2つの異なるデータ型timestamptimestamp without time zone)およびtimestamptztimestamp with time zone)。見る:

  • 特に、timestamptzリテラルのタイムゾーンオフセットは、値への入力/出力修飾子としてのみ機能し、まったく保存されません。内部では、対応するUTC時刻のみが保存されます。見る:

  • POSIXとSQL標準の不一致により、タイムスタンプ literals とタイムゾーン specifiers (反転符号)の間の奇妙な構文の違いに注意してください。

  • show (異なるタイムゾーンオフセットで)の出力は、単にtimestamptz値を返すだけでは達成できません。

    2018-04-05 06:00:00 +01
    2018-04-05 04:00:00 -01
    

    timestamptz値は、セッションのtimezone設定に従って常に displayed です。表示する文字列を取得するには(さまざまなタイムゾーンの場合)、入力のタイムゾーンオフセットを保持し、それを使用して出力をフォーマットする必要があります。 (または完全な入力リテラルをtextとして保存します)

  • 最後に、 '2018-04-05 04:00:00 -01' もテストに合格することに注意してください。これは、が正確にであるためです。 timestamptz値は '2018-04-05 06:00:00 + 01' で、形式が異なります(同じ時点、異なるタイムゾーンで表示)。したがって、3行は、2つだけではなく、例のテストに合格します。

頭が回転している場合は、次のデモを検討してください。

WITH tbl AS (
   SELECT *
        , split_part(tstz_string, ' ', 3) AS tz_string
        , tstz_string::timestamptz AS tstz
   FROM  (
      VALUES 
      (1, '2018-04-05 06:00:00 +01')
    , (2, '2018-04-05 06:00:00 +00')
    , (3, '2018-04-05 06:00:00 -01')
    , (4, '2018-04-05 04:00:00 -01')
      ) t(id, tstz_string)
   )
SELECT *
     , to_char(tz_string::numeric * -1, 'SG00')                                       AS tz_posix
     ,  tstz AT TIME ZONE (tz_string::numeric * -1)::text AS ts_at_org_tz
     , (tstz AT TIME ZONE (tz_string::numeric * -1)::text)::text || ' ' ||  tz_string AS tstz_org
     , (tstz AT TIME ZONE (tz_string::numeric * -1)::text)::text || ' ' ||  tz_string 
      = tstz_string                                                                   AS strings_equal
FROM   tbl;
 id | tstz_string | tz_string | tstz | tz_posix | ts_at_org_tz | tstz_org | strings_equal 
-:| :---------------------- | :-------- | :--------------------- | :------- | :------------------ | :---------------------- | :------------ 
 1 | 2018-04-05 06:00:00 +01 | +01 | 2018-04-05 06:00:00 + 01 | -01 | 2018-04-05 06:00:00 | 2018-04-05 06:00:00 +01 | t 
 2 | 2018-04-05 06:00:00 +00 | +00 | 2018-04-05 07:00:00 + 01 | +00 | 2018-04-05 06:00:00 | 2018-04-05 06:00:00 +00 | t 
 3 | 2018-04-05 06:00:00 -01 | -01 | 2018-04-05 08:00:00 + 01 | +01 | 2018-04-05 06:00:00 | 2018-04-05 06:00:00 -01 | t 
 4 | 2018-04-05 04:00:00 -01 | -01 | 2018-04-05 06:00:00 + 01 | +01 | 2018-04-05 04:00:00 | 2018-04-05 04:00:00 -01 | t 

db <>フィドル ここ

3