理解すべき重要事項
timestamp without time zone AT TIME ZONE
再解釈 timestamp
UTCに変換する目的でそのタイムゾーンにあるものとして 。
timestamp with time zone AT TIME ZONE
変換 timestamptz
timestamp
に 指定されたタイムゾーンで。
PostgreSQLはISO-8601タイムゾーンを使用します。これは、グリニッジの東が正であることを指定します... POSIXタイムゾーン指定子を使用しない限り、POSIXの後に続きます。狂気が続く。
最初のものが予期しない結果をもたらす理由
SQLのタイムスタンプとタイムゾーンはひどいものです。これ:
select '2011-12-30 00:30:00'::timestamp without time zone AT TIME ZONE 'EST5EDT';
不明なタイプのリテラル'2011-12-30 00:30:00'
を解釈します timestamp without time zone
、特に明記されていない限り、Pgはローカルのタイムゾーンにあると想定します。 AT TIME ZONE
を使用する場合 、(仕様に従って)再解釈 timestamp with time zone
として タイムゾーンEST5EDT
次に、UTCで絶対時間として保存されるため、からに変換されます。 EST5EDT
から UTC、つまりタイムゾーンオフセットが減算されます 。 x - (-5)
はx + 5
。
このタイムスタンプはUTCストレージに調整され、サーバーのTimeZone
に合わせて調整されます。 現地時間で表示されるように表示するように設定します。
代わりに、「このタイムスタンプはUTC時間であり、EST5EDTでの同等の現地時間は何かを確認したい」と言いたい場合、サーバーのTimeZone設定から独立したい場合は、次のように記述する必要があります。
select TIMESTAMP '2011-12-30 00:30:00' AT TIME ZONE 'UTC'
AT TIME ZONE 'EST5EDT';
これは、「タイムスタンプ2011-12-30 00:30:00を指定し、タイムスタンプに変換するときにUTCのタイムスタンプとして扱い、そのタイムスタンプをEST5EDTの現地時間に変換する」ことを示しています。
ひどいですね。 しっかりと話したい AT TIME ZONE
のクレイジーなセマンティクスを決定した人 -実際には、timestamp CONVERT FROM TIME ZONE '-5'
のようなものにする必要があります およびtimestamptz CONVERT TO TIME ZONE '+5'
。また、timestamp with time zone
UTCに保存されて現地時間に自動変換されるのではなく、実際にタイムゾーンを保持する必要があります。
2番目が機能する理由(TimeZone =UTCである限り)
オリジナルの「作品」バージョン:
select '2011-12-30 00:30:00' AT TIME ZONE 'EST5EDT';
TimeZoneがUTCに設定されている場合にのみ正しくなります。これは、テキストからタイムスタンプへのキャストでは、TimeZoneが指定されていない場合にTimeZoneが想定されるためです。
3つ目が機能する理由
2つの問題は互いに打ち消し合います。
動作しているように見える他のバージョンはTimeZoneに依存しませんが、2つの問題がキャンセルされるためにのみ動作します。まず、上で説明したように、timestamp without time zone AT TIME ZONE
再解釈 UTCタイムスタンプに変換するためにそのタイムゾーンにあるタイムスタンプ。これは効果的に減算します タイムゾーンオフセット。
ただし、私が自分の知識を超えている理由から、PostgreSQLは、ほとんどの場所を見るのに慣れているものとは逆の符号のタイムスタンプを使用します。ドキュメントを参照してください:
覚えておくべきもう1つの問題は、POSIXタイムゾーン名では、グリニッジの西の場所に正のオフセットが使用されることです。他のすべての場所では、PostgreSQLは正のタイムゾーンオフセットがグリニッジの東にあるというISO-8601規則に従います。
これは、EST5EDT
+5
と同じです 、-5
ではありません 。それが機能する理由です。tzオフセットを加算するのではなく減算しているのに、負のオフセットを減算しているからです!
正しくするために必要なのは、代わりに次のとおりです。
select TIMESTAMP '2011-12-30 00:30:00' AT TIME ZONE 'UTC'
AT TIME ZONE '+5';