今天我們又要來聊聊錯誤了。因為搞日期跟時間,根本就像在踩地雷:一切都很順,直到你第一步踩錯。
選資料型別時的錯誤
這裡常常就是災難的開端。選錯資料型別,會讓你之前處理日期和時間的努力全白費。
情境 1: 用 DATE 取代 TIMESTAMP
如果你記錄的事件不只有日期,還有時間,結果只用 DATE,那你就會遺失重要資訊啦。
CREATE TABLE orders (
order_id SERIAL PRIMARY KEY,
order_date DATE -- 只有日期
);
這樣設計,你根本不知道兩個訂單是早上還是晚上下的。何必放棄喝咖啡時欣賞漂亮 timestamp 的機會呢?
情境 2: 忘記時區
如果你的 app 有國際用戶,但你只用 TIMESTAMP 存日期和時間,沒考慮時區,那你的資料就會變成「無家可歸」。TIMESTAMPTZ 幫你解決這個問題。
CREATE TABLE events (
event_time TIMESTAMP -- 沒有時區
);
沒人想把紐約的晚上活動搞成東京的早上吧。用 TIMESTAMPTZ 才對!
用函式時的錯誤
情境 1: TO_CHAR() 格式寫錯
如果你格式寫錯,問題就來了。例如:
SELECT TO_CHAR(NOW(), 'YYYY-DD-MM'); -- 哎呀,月跟日寫反了
這裡你本來想要 年-月-日,結果變成 年-日-月。這會讓用戶遇到很搞笑(但不一定開心)的狀況。格式一定要檢查清楚。
情境 2: 用 TO_DATE() 時出錯
反過來,如果你想把字串轉成日期,但格式不對,PostgreSQL 會直接報錯。
SELECT TO_DATE('10/31/2023', 'YYYY-MM-DD'); -- 錯誤!格式不符。
字串格式一定要跟你指定的完全一樣。例如:
SELECT TO_DATE('2023-10-31', 'YYYY-MM-DD'); -- 這樣才對。
處理時間區間時的錯誤
情境 1: 隱式型別轉換
有時候你會忘記型別轉換的細節。例如:
SELECT NOW() + '1'; -- 錯誤!'1' 是啥?
PostgreSQL 不知道你想加一天。正確寫法:
SELECT NOW() + INTERVAL '1 day';
情境 2: 區間減法搞混
加減區間時要小心:
SELECT NOW() - INTERVAL '-1 day'; -- 這其實是加一天!
這裡雙負號反而變成加。這種寫法最好別用。
四捨五入和截斷資料時的錯誤
情境 1: DATE_TRUNC() 截錯
用 DATE_TRUNC() 做資料分組時,一定要確認你選對層級。例如:
SELECT DATE_TRUNC('hour', NOW()); -- 截到整點
SELECT DATE_TRUNC('minute', NOW()); -- 截到整分鐘
如果你預期一個結果,卻拿到另一個,可能就是層級選錯了。
情境 2: DATE_TRUNC() 忘記時區
如果你處理不同時區的時間,結果可能會很意外:
SELECT DATE_TRUNC('day', NOW() AT TIME ZONE 'UTC');
一定要確認時區設對,不然真的會「迷失在時間裡」!
Unix 時間:消失的秒數
Unix 時間(EPOCH)很方便,但也很陰險。最常見的錯誤就是把秒跟毫秒搞混。
SELECT TO_TIMESTAMP(1680000000); -- 這樣對(秒)。
SELECT TO_TIMESTAMP(1680000000000); -- 這樣錯!太多零了。
一定要確認你的 timestamp 單位,不然會多存一大堆多餘的秒數。
時區相關的錯誤
情境 1: 時區搞混
如果你有來自不同時區的用戶,資料很容易混在一起。例如:
SELECT TIMESTAMP '2023-10-01 10:00:00' AT TIME ZONE 'UTC';
一定要搞清楚你的資料到底是哪個時區。
情境 2: 時區重複計算
把日期時間存進去後又再處理時區,這樣很糟:
SELECT TIMESTAMP '2023-10-01 10:00:00 UTC' AT TIME ZONE 'UTC'; -- 千萬別這樣!
這會讓你的計算全錯。
避免錯誤的建議
選對資料型別。 如果你要處理國際時間,請用 TIMESTAMPTZ。只要日期就用 DATE。
多測試你的查詢。 一定要確認結果跟你預期一樣,尤其是處理區間、格式或四捨五入時。
用 UTC 存時間。 這是避免時區混亂的最佳做法。
檢查格式。 TO_CHAR() 和 TO_DATE() 的格式一定要跟你的資料一致。
小心用函式。 仔細看 PostgreSQL 時間函式文件,才不會被嚇到。
處理時間資料真的不簡單,但只要方法對、細節顧好,一切都會很順。日期和時間是你 app 的重要一環,忘了它們就像週一早上忘記設鬧鐘一樣危險!
GO TO FULL VERSION