web-dev-qa-db-ja.com

postgresのタイムスタンプを「1日の終わり」に制限しますか?

指定bananasという名前のテーブル
およびtimestamp without time zoneという名前のend_time
およびsomepsqlクライアントはset timezone to 'UTC'
およびその他psqlクライアントはset timezone to 'US/Eastern'
andサーバー構成にはtimezone = 'UTC'postgresql.confがあります

bananas.end_timeにチェック制約を記述して、end_timeが常に1日の終わりになるようにするには、"US/Easternの23時間59分59秒として定義します。 "日?

私は試した:

alter table bananas
add constraint ck_end_time_is_end_of_day
check (
  23 = date_part('hour', end_time at time zone 'UTC' at time zone 'US/Eastern')
  and 59 = date_part('minute', end_time at time zone 'UTC' at time zone 'US/Eastern') 
  and 59 = floor(date_part('second', end_time at time zone 'UTC' at time zone 'US/Eastern')) 
)
;

これは列を正しく制限しているように見えますが、恐ろしく非効率的であり、まったく読めないようです。より効率的で読みやすい実装はありますか?

3
Jared Beck

今のところ

あなたの不幸な解決策で立ち往生している間:

_CHECK ((end_time AT TIME ZONE 'UTC' AT TIME ZONE 'US/Eastern')::time = '23:59:59'::time)
_

そうです、_AT TIME ZONE_ two回:

  • 最初のインスタンスは_timestamp without time zone_を_timestamp with time zone_に変換します。これは、実際にUTC時間を格納していることを前提としています。

  • 2番目のインスタンスは、timestamptztimestampに変換し直します指定されたタイムゾーンで。これで、timeコンポーネントが希望どおりであることを確認できます。

文字列に変換する代わりに、timeにキャストすると、より安価で堅牢になります。

小数桁を取り除くには、代わりにtime(0)にキャストしますが、roundではなくfloorあなたの例では。代わりに、 date_trunc() で切り捨てます。これは、正の数のfloor()の安価な方法です。

_CHECK ((date_trunc('sec', end_time) AT TIME ZONE 'UTC' AT TIME ZONE 'US/Eastern')::time
        = '23:59:59'::time)_

適切なソリューション

timestampの値は小数桁であり、時間コンポーネント_'23:59:59'_の使用は上限が不幸な決定であるためです。代わりに、次の日の_00:00_を排他的な上限として使用を使用します。 thatをチェック制約で強制するのは簡単です。

次に、複数のタイムゾーンを扱っているので、timestamptzの代わりに timestamp を使用することをお勧めします。内部ストレージは、指定されたタイムゾーン「UTC」のtimestampと同じですが、入出力の処理が異なります。

  • タイムスタンプは、出力時に現在のタイムゾーンに自動的にシフトされます。
  • タイムゾーンオフセットを使用してタイムスタンプを入力し、値がUTC時間に従って自動的に保存されるようにします。

SOの詳細と関連する回答)の関連回答

5

to_charを使用して、単一の関数呼び出しから時間フィールドを取得できます。

check (to_char(end_time at time zone 'UTC' at time zone 'US/Eastern','HH24:MI:SS') = '23:59:59')

SSで指定された秒は切り上げられないため、floorと同等で問題ありません。

3
Daniel Vérité

これを試して。おそらくもっと速いですが、それでも判読できません:

alter table bananas
add constraint ck_end_time_is_end_of_day
check (
  (end_time at time zone 'UTC' at time zone 'US/Eastern')::timestamp::date + interval '23:59:59' = 
   date_trunc('second', end_time at time zone 'UTC' at time zone 'US/Eastern')
)
;
0
cha