Oracle 日期運算是開發中最高頻操作之一。本文系統整理年/月/周/日/時/分/秒各級別加減運算、ADD_MONTHS 特性、間隔類型應用、格式化輸出及常見陷阱。
| 運算類型 | 語法 | 適用場景 | 注意事項 |
|---|---|---|---|
| 月份加減 | ADD_MONTHS(date, n) | 年/月增減 | 自動處理月末邊界(如 1/31 + 1月 = 2/28 或 2/29) |
| 天數加減 | date + n / date - n | 日/周增減 | 整數代表天,小數代表分數天 |
| 間隔運算 | date + INTERVAL 'n' UNIT | 精確時/分/秒/年/月 | 10g+ 支持,返回 TIMESTAMP WITH TIME ZONE |
| 數值轉換 | date + n/24/60/60 | 時/分/秒精確運算 | 傳統寫法,兼容性好 |
-- 加 1 年
SELECT SYSDATE, ADD_MONTHS(SYSDATE, 12) FROM DUAL;
-- 加 1 月
SELECT SYSDATE, ADD_MONTHS(SYSDATE, 1) FROM DUAL;
-- 加 6 月
SELECT SYSDATE, ADD_MONTHS(SYSDATE, 6) FROM DUAL;
-- 加 1 週
SELECT SYSDATE, SYSDATE + 7 FROM DUAL;
-- 加 1 天
SELECT SYSDATE, SYSDATE + 1 FROM DUAL;
-- 加 3 天
SELECT SYSDATE, SYSDATE + 3 FROM DUAL;
-- 加 1 小時 (1/24 天)
SELECT SYSDATE, SYSDATE + 1/24 FROM DUAL;
-- 加 30 分鐘 (30/24/60 = 1/48 天)
SELECT SYSDATE, SYSDATE + 1/48 FROM DUAL;
-- 加 1 分鐘 (1/24/60 天)
SELECT SYSDATE, SYSDATE + 1/24/60 FROM DUAL;
-- 加 1 秒 (1/24/60/60 天)
SELECT SYSDATE, SYSDATE + 1/24/60/60 FROM DUAL;
SELECT TO_CHAR(SYSDATE + 1/24, 'YYYY-MM-DD HH24:MI:SS') AS one_hour_later FROM DUAL;
-- 減 1 年
SELECT SYSDATE, ADD_MONTHS(SYSDATE, -12) FROM DUAL;
-- 減 1 月
SELECT SYSDATE, ADD_MONTHS(SYSDATE, -1) FROM DUAL;
-- 減 1 週
SELECT SYSDATE, SYSDATE - 7 FROM DUAL;
-- 減 1 天
SELECT SYSDATE, SYSDATE - 1 FROM DUAL;
-- 減 1 小時
SELECT SYSDATE, SYSDATE - 1/24 FROM DUAL;
-- 減 1 分鐘
SELECT SYSDATE, SYSDATE - 1/24/60 FROM DUAL;
-- 減 1 秒
SELECT SYSDATE, SYSDATE - 1/24/60/60 FROM DUAL;
-- 假設 ship_date 為 DATE 類型,total_times 為運輸小時數
SELECT
ship_date,
total_times,
TO_CHAR(ship_date + total_times/24, 'DD/MM/YYYY HH24:MI') AS estimated_arrival
FROM shipments;
-- sign_in_date - (ship_date + total_times/24) 得到天數差,×24 得小時
SELECT
TO_CHAR(ROUND((sign_in_date - (ship_date + total_times/24)) * 24)) AS delay_hours
FROM shipments;
-- TRUNC 截斷到天
SELECT TRUNC(SYSDATE) FROM DUAL; -- 返回當天 00:00:00
-- TRUNC 截斷到月
SELECT TRUNC(SYSDATE, 'MM') FROM DUAL; -- 返回本月 1 號 00:00:00
-- TRUNC 截斷到年
SELECT TRUNC(SYSDATE, 'YYYY') FROM DUAL;
-- EXTRACT (推薦,語義清晰)
SELECT
EXTRACT(YEAR FROM SYSDATE) AS yr,
EXTRACT(MONTH FROM SYSDATE) AS mon,
EXTRACT(DAY FROM SYSDATE) AS day,
EXTRACT(HOUR FROM CAST(SYSDATE AS TIMESTAMP)) AS hr,
EXTRACT(MINUTE FROM CAST(SYSDATE AS TIMESTAMP)) AS mi
FROM DUAL;
-- TO_CHAR (格式化靈活)
SELECT
TO_CHAR(SYSDATE, 'YYYY') AS yr,
TO_CHAR(SYSDATE, 'MM') AS mon,
TO_CHAR(SYSDATE, 'DD') AS day,
TO_CHAR(SYSDATE, 'DAY') AS weekday,
TO_CHAR(SYSDATE, 'HH24') AS hr
FROM DUAL;
-- 標準格式
TO_DATE('2024-01-15', 'YYYY-MM-DD')
-- 含時間
TO_DATE('2024-01-15 14:30:00', 'YYYY-MM-DD HH24:MI:SS')
-- 不區分大小寫
TO_DATE('2024-01-15', 'yyyy-mm-dd')
SELECT TO_CHAR(TO_DATE('2024-01-15', 'YYYY-MM-DD'), 'DAY') FROM DUAL; -- MONDAY
SELECT TO_CHAR(TO_DATE('2024-01-15', 'YYYY-MM-DD'), 'DY') FROM DUAL; -- MON
SELECT SYSDATE + INTERVAL '1' YEAR FROM DUAL;
SELECT SYSDATE + INTERVAL '3' MONTH FROM DUAL;
SELECT SYSDATE + INTERVAL '1-6' YEAR TO MONTH FROM DUAL; -- 1 年 6 月
SELECT SYSDATE + INTERVAL '7' DAY FROM DUAL;
SELECT SYSDATE + INTERVAL '12' HOUR FROM DUAL;
SELECT SYSDATE + INTERVAL '30' MINUTE FROM DUAL;
SELECT SYSDATE + INTERVAL '45' SECOND FROM DUAL;
SELECT SYSDATE + INTERVAL '1 12:30:00' DAY TO SECOND FROM DUAL; -- 1 天 12 時 30 分
| 陷阱 | 錯誤示例 | 正確做法 |
|---|---|---|
| 隱式轉換依賴 NLS 設置 | WHERE dt = '2024-01-15' | WHERE dt = TO_DATE('2024-01-15', 'YYYY-MM-DD') |
| 月末日期加月邏輯錯誤 | DATE '2024-01-31' + 1 月 → 錯誤或 2/28 | ADD_MONTHS(DATE '2024-01-31', 1) → 2024-02-29 |
| TIMESTAMP 與 DATE 混用精度丟失 | ts - dt 返回 INTERVAL DAY TO SECOND | CAST(dt AS TIMESTAMP) 或 CAST(ts AS DATE) |
| 分數天精度問題 | 1/24/60/60 可能產生浮點誤差 | 使用 INTERVAL 或 NUMTODSINTERVAL |
Oracle 日期運算看似簡單,但邊界處理、類型轉換、性能影響等細節決定代碼質量。建議:優先使用 ADD_MONTHS 處理年月、INTERVAL 處理精確時間、TRUNC 截斷比較、索引列拒絕函數運算,建立團隊統一日期處理規範。