Postgresql
將 postgres 時間戳限制為“一天結束”?
給定一個名為的表
bananas
和一個名為的
timestamp without time zone
列end_time
, 一些
psql
客戶端有set timezone to 'UTC'
, 其他
psql
客戶端有set timezone to 'US/Eastern'
**,**伺服器配置
timezone = 'UTC'
在postgresql.conf
如何編寫檢查約束
bananas.end_time
以確保end_time
始終是一天的結束,定義為**“美國/東部”**一天的第 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')) ) ;
這似乎正確地限制了列,但它似乎效率低得可怕,而且非常不可讀。是否有更高效和/或更易讀的實現?
目前
雖然堅持你不幸的解決方案:
CHECK ((end_time AT TIME ZONE 'UTC' AT TIME ZONE 'US/Eastern')::time = '23:59:59'::time)
沒錯,
AT TIME ZONE
兩次:
- 第一個實例將您
timestamp without time zone
轉換為timestamp with time zone
. 這是假設您實際上是在儲存 UTC 時間。- 第二個實例將
timestamptz
back轉換timestamp
為您給定的 time zone。現在您可以檢查該time
組件是否符合您的要求。Cast to
time
,而不是轉換為字元串,這更便宜且更健壯。要擺脫小數位,您可以**
time(0)
**改為轉換為,但在您的範例中,這會舍入值而不是下限。相反,用 截斷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
第二天作為排他的上邊界**。使用檢查約束來強制執行它是微不足道的。接下來,由於您要處理多個時區,我建議使用
timestamptz
而不是timestamp
.timestamp
內部儲存與給定時區“UTC”中的儲存相同,但輸入/輸出處理不同。
- 時間戳在輸出時自動轉移到目前時區。
- 輸入帶有時區偏移的時間戳,以便將值自動保存為根據 UTC 時間。