Postgresから日付を適切に選択する際に問題が発生しています。日付はUTCで保存されていますが、Date()関数で正しく変換されていません。
タイムスタンプを日付に変換すると、PSTの午後4時を過ぎた場合、間違った日付が表示されます。
2012-06-21
は2012-06-20
この場合。
starts_at
列のデータ型はtimestamp without time zone
。私のクエリは次のとおりです。
PSTタイムゾーンに変換しない場合:
Select starts_at from schedules where id = 40;
starts_at
---------------------
2012-06-21 01:00:00
変換するとこれが得られます:
Select (starts_at at time zone 'pst') from schedules where id = 40;
timezone
------------------------
2012-06-21 02:00:00-07
ただし、タイムゾーンの正しい日付に変換することはできません。
質問にexactタイプのstarts_at
が表示されません。本当にこの情報を含める必要があります、それはソリューションへの鍵です。推測する必要があります。
基本的に、PostgreSQLalwaysはtimestamp with time zone
型のUTC時間値を内部的に保存します。現在のtimezone
設定によって表示のみが異なります。 AT TIME ZONE
構造の効果も、基礎となるデータ型によって変わります。詳細:
タイプ timestamp [without time zone]
からdate
を抽出すると、現在のタイムゾーンの日付が取得されます。出力の日は、timestamp
値の表示と同じになります。
タイプ timestamp with time zone
(略してdate
)からtimestamptz
を抽出すると、タイムゾーンオフセットが最初に「適用」されます。タイムスタンプのdisplayと一致する現在のタイムゾーンの日付を取得します。同じ時点が午後4時を過ぎると、ヨーロッパの一部の地域の次の日に変換されます。たとえばカリフォルニア。特定のタイムゾーンの日付を取得するには、最初にAT TIME ZONE
を適用します。
したがって、質問の冒頭で説明する内容は、あなたの例と矛盾します。
starts_at
がtimestamp [without time zone]
であり、サーバーの時刻がローカル時刻に設定されている場合。でテストする:
SELECT now();
壁の時計と同じ時間を表示しますか?はい(およびdbサーバーが正しい時間で実行されている)場合、現在のセッションのtimezone
設定はローカルタイムゾーンと一致します。いいえの場合、セッションのpostgresql.conf
またはクライアントのtimezone
の設定にアクセスすることをお勧めします。 マニュアルの詳細
timezone
オフセットは、タイムスタンプリテラルに表示されるものの反対signを使用したことに注意してください。見る:
starts_at
からローカル日付を取得するには
SELECT starts_at::date
同等:
SELECT date(starts_at)
ところで、夏時間は有効であるため、現地時間はUTC-8ではなくUTC-7になります(人類の明るいアイデアではありません)。
太平洋標準時(PST)は通常、UTC(ユニバーサルタイムゾーン)よりも8時間「早い」(timestamp
値が大きい)ですが、夏時間(現在のように)では7時間になることがあります。そのため、timestamptz
が2012-06-21 02:00:00
-07
として表示されます。コンストラクトAT TIME ZONE 'PST'
は夏時間を考慮に入れます。これら2つの式は異なる結果(冬に1つ、夏に1つ)を生成し、キャスト時に異なる日付になる可能性があります。
SELECT '2012-06-21 01:00:00'::timestamp AT TIME ZONE 'PST'
, '2012-12-21 01:00:00'::timestamp AT TIME ZONE 'PST'
基本的にあなたが望むのは:
$ select starts_at AT TIME ZONE 'UTC' AT TIME ZONE 'US/Pacific' from schedules where id = 40
私はこの記事から解決策を得ましたが、それはストレートゴールドです!!!この重要でない問題を非常に明確に説明しており、pstgrsql TZ管理をよりよく理解したい場合は読んでください。
現地時間でゾーンなしでPostgreSQLタイムスタンプを表現する
これが何が起こっているかです。まず、「PSTタイムゾーンはUTCタイムゾーンから8時間遅れているため、たとえば2014年1月1日、4:30 PM PST(Wed、01 Jan 2014 16:00:30 -0800)は2014年1月2日、UTC 00:30 AM(木、2014年1月2日00:00:30 +0000)に相当PSTの午後4時以降は、UTCと解釈されて翌日にスリップします。
また、前述のErwin Brandstetterが述べたように、postresqlには2つのタイプのタイムスタンプデータ型があります。1つはタイムゾーンがあり、もう1つはありません。タイムスタンプにタイムゾーンが含まれている場合、次のように簡単です:
$ select starts_at AT TIME ZONE 'US/Pacific' from schedules where id = 40
働くでしょう。ただし、タイムスタンプがタイムゾーンなしの場合、上記のコマンドの実行は機能せず、タイムゾーンなしのタイムスタンプをタイムゾーン付きのタイムスタンプ、つまりUTCタイムゾーンに最初に変換し、次にそれを目的の「PST」または「US/Pacific '(夏時間の問題までは同じです。どちらでも大丈夫だと思います)。
タイムゾーンのないタイムスタンプを作成する例を示しましょう。便宜上、ローカルタイムゾーンが実際に「PST」であると仮定しましょう(そうでない場合は、この説明の目的には不要な、もう少し複雑になります)。
私が持っていると言います:
$ select timestamp '2014-01-2 00:30:00' AS a, timestamp '2014-01-2 00:30:00' AT TIME ZONE 'UTC' AS b, timestamp '2014-01-2 00:30:00' AT TIME ZONE 'UTC' AT TIME ZONE 'PST' AS c, timestamp '2014-01-2 00:30:00' AT TIME ZONE 'PST' AS d
これにより以下が得られます。
"a"=>"2014-01-02 00:30:00" (This is the timezoneless timestamp)
"b"=>"2014-01-02 00:30:00+00" (This is the UTC TZ timestamp, note that up to a timezone, it is equivalent to the timezoneless one)
"c"=>"2014-01-01 16:30:00" (This is the correct 'PST' TZ conversion of the UTC timezone, if you read the documentation postgresql will not print the actual TZ for this conversion)
"d"=>"2014-01-02 08:30:00+00"
最後のタイムスタンプは、postgresqlでタイムゾーンのないタイムスタンプをUTCから「PST」に変換することに関するすべての混乱の理由です。私たちが書くとき:
timestamp '2014-01-2 00:30:00' AT TIME ZONE 'PST' AS d
タイムゾーンのないタイムスタンプを取得して、PST TZに変換しようとしています(postgresqlがUTC TZからタイムスタンプを変換することを理解していると間接的に想定していますが、postresqlには独自の計画があります!)。実際には、postgresqlが行うことは、タイムゾーンのないタイムスタンプ( '2014-01-2 00:30:00)を取得し、それが既に' PST 'TZタイムスタンプであるかのように処理します(つまり:2014-01-2 00:30 :00 -0800)とUTCタイムゾーンに変換します!!!したがって、実際には、戻るのではなく、8時間前にプッシュします!したがって、(2014-01-02 08:30:00 + 00)になります。
とにかく、この最後の(直感的でない)動作がすべての混乱の原因です。より徹底的な説明が必要な場合は、この記事を読んでください。実際、この最後の部分とは少し異なる結果が得られましたが、一般的な考え方は同じです。
これは古いものですが、PST/PDTの問題を回避するためにキャストするときにAT TIME ZONE "US/Pacific"を使用することを検討することをお勧めします。
SELECT starts_at :: TIMESTAMPTZ AT TIME ZONE "US/Pacific" FROMスケジュールWHERE ID = '40';
cast(master.Stamp5DateTime as date) >= '05-05-2019' AND
cast(master.Stamp5DateTime as date) <= '05-05-2019'