今朝、次の問題に直面しました。
select '2011-12-30 00:30:00'::timestamp without time zone AT TIME ZONE 'EST5EDT';
私を返します2011-12-30 05:30:00+00
魔女は間違っています。
しかし、次のクエリは次のとおりです。
select '2011-12-30 00:30:00'::timestamp without time zone AT TIME ZONE 'UTC-5';
select '2011-12-30 00:30:00' AT TIME ZONE 'EST5EDT';
正しい日付が表示されます2011-12-29 19:30:00
私のローカルタイムゾーンについての質問を防ぐ:
SELECT current_setting('TIMEZONE');
current_setting
-----------------
UTC
(1 row)
Postgresqlがtimestamp without time zone
いくつかの奇妙な方法で、代わりに5時間を費やしますか?
_timestamp without time zone AT TIME ZONE
_ re-interprets a timestamp
はそのタイムゾーンにあるとTCに変換するために。
_timestamp with time zone AT TIME ZONE
_ converts a指定されたタイムゾーンで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は、特に指定がない限り、ローカルのTimeZoneにあると想定します。 _AT TIME ZONE
_を使用すると、(仕様により)re-interpretedとしてタイムゾーン_timestamp with time zone
_として_EST5EDT
_になり、UTCの絶対時間として保存されます-したがって、変換されますfrom _EST5EDT
_ to 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を指定し、timestamptzに変換するときにUTCのタイムスタンプとして処理し、EST5EDTでそのtimestamptzを現地時間に変換します」と言います。
恐ろしいですね。 _AT TIME ZONE
_のクレイジーなセマンティクスを決定した人は誰でも会社と話し合うを送りたい-それは本当に_timestamp CONVERT FROM TIME ZONE '-5'
_や_timestamptz CONVERT TO TIME ZONE '+5'
_のようなものであるべきだ。また、_timestamp with time zone
_は実際にタイムゾーンを保持する必要があります。UTCに保存されず、ローカル時間に自動変換されます。
オリジナルの「作品」バージョン:
_select '2011-12-30 00:30:00' AT TIME ZONE 'EST5EDT';
_
timeZoneがUTCに設定されている場合のみ、テキストからタイムスタンプへのキャストは、指定されていない場合はTimeZoneを想定するため、正確になります。
2つの問題が互いに相殺します。
動作しているように見えるもう1つのバージョンはTimeZoneに依存しませんが、2つの問題が解消されるためにのみ動作します。まず、上で説明したように、_timestamp without time zone AT TIME ZONE
_ re-interprets UTCタイムスタンプに変換するためのタイムゾーンにあるタイムスタンプ。これは実質的にsubtractsタイムゾーンオフセットです。
しかし、私は私の理由を超えて、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';
_