日期時間處理是資料庫開發的高頻痛點。Oracle 的 TO_DATE(字串→日期)、TO_CHAR(日期→字串)雖功能強大,但格式模型對大小寫不敏感、分鐘用 MI 非 MM、24 小時制用 HH24 等細節極易踩坑。本文系統整理格式元素對照表、常見錯誤與解法、間隔運算、毫秒處理、效能測量等實戰技巧。
| 元素 | 含義 | 範例輸出 |
|---|---|---|
| YYYY / YY / Y | 4/2/1 位年份 | 2024 / 24 / 4 |
| MM / MON / MONTH | 月份:數字/縮寫/全名 | 01 / JAN / JANUARY |
| DD / DDD | 月內第幾天 / 年內第幾天 | 15 / 045 |
| DAY / DY / D | 星期名/縮寫/數字(1-7) | MONDAY / MON / 2 |
| HH / HH12 | 12 小時制 (1-12) | 03 |
| HH24 | 24 小時制 (0-23) | 15 |
| MI | 分鐘 (0-59) —— 關鍵:非 MM | 07 |
| SS | 秒 (0-59) | 30 |
| FF / FF3 / FF6 | 小數秒/毫秒/微秒 | .123456 |
| W / WW / IW | 月內週/年內週/ISO 週 | 3 / 12 / 15 |
-- 錯誤:MM 被視為月份,導致 "format code appears twice"
SELECT TO_DATE('2005-01-01 13:14:20', 'yyyy-MM-dd HH24:mm:ss') FROM dual;
-- ORA-01810: format code appears twice
-- 正確:分鐘用 MI
SELECT TO_DATE('2005-01-01 13:14:20', 'yyyy-MM-dd HH24:mi:ss') FROM dual;
-- 2005-01-01 13:14:20
原因: Oracle 格式模型**不分大小寫**,MM (月) 與 mm (月) 衝突,必須用 MI 表分鐘。
-- 預設左補零/空格
SELECT TO_CHAR(SYSDATE, 'YYYY-MM-DD HH24:MI:SS') FROM dual; -- 2024-01-05 03:07:09
-- FM 移除前導零/空格
SELECT TO_CHAR(SYSDATE, 'FMYYYY-MM-DD FMHH24:MI:SS') FROM dual; -- 2024-1-5 3:7:9
-- 貨幣/數字格式同理
SELECT TO_CHAR(1234.5, 'FML99G999D99') FROM dual; -- $1,234.50 (依 NLS_TERRITORY)
-- 減去 7 分鐘/小時/天/月/年
SELECT SYSDATE - INTERVAL '7' MINUTE FROM dual;
SELECT SYSDATE - INTERVAL '7' HOUR FROM dual;
SELECT SYSDATE - INTERVAL '7' DAY FROM dual;
SELECT SYSDATE - INTERVAL '7' MONTH FROM dual;
SELECT SYSDATE - INTERVAL '7' YEAR FROM dual;
-- 間隔乘法
SELECT SYSDATE - 8 * INTERVAL '2' HOUR FROM dual; -- 減 16 小時
-- 加法同理
SELECT SYSDATE + INTERVAL '3' DAY FROM dual;
優點:語意清晰、自動處理月份天數差異、支援年月間隔(INTERVAL '1-6' YEAR TO MONTH)。
-- 截取到年/月/日/時/分開頭
SELECT TRUNC(SYSDATE, 'YEAR') FROM dual; -- 2024-01-01 00:00:00
SELECT TRUNC(SYSDATE, 'MONTH') FROM dual; -- 2024-01-01 00:00:00
SELECT TRUNC(SYSDATE, 'DAY') FROM dual; -- 本週週一 00:00:00
SELECT TRUNC(SYSDATE, 'HH') FROM dual; -- 當前小時 00 分
SELECT TRUNC(SYSDATE) FROM dual; -- 當天 00:00:00
-- ROUND 到最近邊界
SELECT ROUND(SYSDATE, 'MONTH') FROM dual; -- 15 號後進入下月
-- CURRENT_TIMESTAMP 回傳 TIMESTAMP WITH TIME ZONE
SELECT TO_CHAR(CURRENT_TIMESTAMP(6), 'YYYY-MM-DD HH24:MI:SS.FF6') FROM dual;
-- 2024-01-15 14:30:45.123456
-- 僅取秒.微秒
SELECT TO_CHAR(CURRENT_TIMESTAMP(9), 'MI:SS.FF9') FROM dual;
CURRENT_TIMESTAMP(precision) 精度 0-9,預設 6(微秒)。
DECLARE
l_start NUMBER := DBMS_UTILITY.GET_TIME; -- 百分之一秒
l_dummy ALL_OBJECTS.OBJECT_NAME%TYPE;
l_rc SYS_REFCURSOR;
BEGIN
FOR i IN 1..1000 LOOP
OPEN l_rc FOR 'SELECT object_name FROM all_objects WHERE object_id = ' || i;
FETCH l_rc INTO l_dummy;
CLOSE l_rc;
END LOOP;
DBMS_OUTPUT.PUT_LINE('耗時: ' || ROUND((DBMS_UTILITY.GET_TIME - l_start)/100, 2) || ' 秒');
END;
/
-- 輸出: 耗時: 1.23 秒
DBMS_UTILITY.GET_TIME 回傳百分之一秒,除以 100 得秒,精度約 10ms。更高精度可用 DBMS_UTILITY.GET_CPU_TIME 或 Java System.nanoTime()。
NLS_DATE_FORMAT 隱性轉換。HH24 避免 AM/PM 歧義。DATE_FORMAT 參數,避免逐行 TO_DATE 開銷。掌握這套日期轉換知識體系,能從根源消除 90% 以上的日期處理 Bug,顯著提升開發效率與系統穩定性。