核心函數分類速查表
| 類別 | 函數 | 語法 | 返回 | 典型用途 |
| 獲取/構造 | SYSDATE | SYSDATE | DATE | 資料庫服務器當前日期時間 |
| SYSTIMESTAMP | SYSTIMESTAMP | TIMESTAMP WITH TIME ZONE | 高精度+時區 |
| CURRENT_DATE | CURRENT_DATE | DATE | 會話時區當前日期 |
| CURRENT_TIMESTAMP | CURRENT_TIMESTAMP | TIMESTAMP WITH TIME ZONE | 會話時區高精度 |
| LOCALTIMESTAMP | LOCALTIMESTAMP | TIMESTAMP | 會話時區無時區戳 |
| DBTIMEZONE | DBTIMEZONE | VARCHAR2 | 資料庫時區偏移 |
| SESSIONTIMEZONE | SESSIONTIMEZONE | VARCHAR2 | 會話時區偏移 |
| TO_DATE | TO_DATE(str, fmt) | DATE | 字符串轉日期 |
| 運算/偏移 | ADD_MONTHS | ADD_MONTHS(d, n) | DATE | 月份加減(自動處理月末) |
| LAST_DAY | LAST_DAY(d) | DATE | 所在月最後一天 |
| NEXT_DAY | NEXT_DAY(d, 'FRIDAY') | DATE | 下一個指定週幾 |
| MONTHS_BETWEEN | MONTHS_BETWEEN(d1, d2) | NUMBER | 月份差(含小數) |
| NEW_TIME | NEW_TIME(d, 'GMT', 'EST') | DATE | 跨時區轉換(舊函數) |
| ROUND | ROUND(d, 'MM') | DATE | 四捨五入到月/年/日 |
| TRUNC | TRUNC(d, 'MM') | DATE | 截斷到月/年/日首 |
| GREATEST/LEAST | GREATEST(d1, d2...) | DATE | 最大/最小日期 |
| 格式化/轉換 | TO_CHAR | TO_CHAR(d, fmt) | VARCHAR2 | 日期轉字符串(報表核心) |
| TO_TIMESTAMP | TO_TIMESTAMP(str, fmt) | TIMESTAMP | 字符串轉時間戳 |
| CAST | CAST(d AS TIMESTAMP) | TIMESTAMP | 類型轉換 |
| EXTRACT | EXTRACT(YEAR FROM d) | NUMBER | 提取年/月/日/時/分/秒 |
| 間隔運算 | NUMTODSINTERVAL | NUMTODSINTERVAL(n, 'DAY') | INTERVAL DAY TO SECOND | 數值轉日間隔 |
| NUMTOYMINTERVAL | NUMTOYMINTERVAL(n, 'MONTH') | INTERVAL YEAR TO MONTH | 數值轉年月間隔 |
| + / - | d + 1.5, d - INTERVAL '1' DAY | DATE/TIMESTAMP | 日期算術 |
格式模型元素速查(TO_CHAR/TO_DATE 通用)
| 元素 | 含義 | 範例 |
| YYYY / RRRR | 4 位年份(RRRR 兩位年份自動推斷世紀) | 2024 / 24→2024 |
| MM / MON / MONTH | 月份 數字/縮寫/全拼 | 05 / MAY / MAY |
| DD / DDD | 月內天 / 年內天 | 17 / 138 |
| HH24 / HH12 | 24/12 小時制 | 14 / 02 |
| MI / SS / FFn | 分/秒/毫微秒(n 位) | 30 / 45 / .123456 |
| DAY / DY / D | 星期全拼/縮寫/數字(1-7) | FRIDAY / FRI / 6 |
| AM / PM / A.M. | 上午/下午標記 | PM |
| TZH / TZM / TZR | 時區時/分/區域名 | +08 / 00 / Asia/Shanghai |
實戰組合拳範例
1. 本月第一天/最後一天 00:00:00
TRUNC(SYSDATE, 'MM') -- 本月 1 號 00:00
LAST_DAY(TRUNC(SYSDATE)) + 1 - 1/86400 -- 本月最後一天 23:59:59 (舊寫法)
TRUNC(ADD_MONTHS(SYSDATE, 1), 'MM') - 1/86400 -- 更優雅
2. 精確天數差(含時分秒)
-- 兩個 DATE 相減 = 天數 (含小數)
SELECT (SYSDATE - TO_DATE('2024-01-01','YYYY-MM-DD')) AS days_diff FROM dual;
-- 僅整天
SELECT FLOOR(SYSDATE - TO_DATE('2024-01-01','YYYY-MM-DD')) FROM dual;
3. 年齡計算(精確到年月日)
SELECT
FLOOR(MONTHS_BETWEEN(SYSDATE, birth_dt)/12) AS years,
MOD(FLOOR(MONTHS_BETWEEN(SYSDATE, birth_dt)), 12) AS months,
TRUNC(SYSDATE - ADD_MONTHS(birth_dt,
FLOOR(MONTHS_BETWEEN(SYSDATE, birth_dt)/12)*12 +
MOD(FLOOR(MONTHS_BETWEEN(SYSDATE, birth_dt)), 12)
)) AS days
FROM employees;
4. 閏年判斷
-- 方法 1:2 月最後一天是否 29
CASE WHEN TO_CHAR(LAST_DAY(TO_DATE('02'||:yr,'MMYYYY')), 'DD') = '29' THEN 1 ELSE 0 END
-- 方法 2:年份規則
CASE WHEN MOD(:yr, 400) = 0 OR (MOD(:yr, 4) = 0 AND MOD(:yr, 100) != 0) THEN 1 ELSE 0 END
5. 分頁時間窗口(避免隱式轉換導致索引失效)
-- 錯誤:WHERE create_dt > '2024-01-01' -- 字符串隱式轉換
-- 正確:
WHERE create_dt >= TO_DATE('2024-01-01', 'YYYY-MM-DD')
AND create_dt < TO_DATE('2024-02-01', 'YYYY-MM-DD') -- 閉區間 [start, end)
6. 時區感知轉換(11g+ 推薦)
-- 將 UTC 時間戳轉為上海時間
SELECT CAST(FROM_TZ(CAST(utc_ts AS TIMESTAMP), 'UTC') AT TIME ZONE 'Asia/Shanghai' AS TIMESTAMP)
FROM events;
-- 會話時區設置
ALTER SESSION SET TIME_ZONE = 'Asia/Shanghai';
7. 脫敏:只保留年月
SELECT TO_CHAR(birth_dt, 'YYYY-MM') FROM patients; -- 2024-05
常見陷阱與避坑
| 陷阱 | 現象 | 規避 |
| 隱式轉換 | WHERE dt = '2024-01-01' 導致全表掃描 | 強制 TO_DATE + 格式模型 |
| RRRR vs YYYY | 兩位年份 99→1999, 01→2001 (RRRR) vs 0099/0001 (YYYY) | 業務明確用 YYYY,模糊輸入用 RRRR |
| NLS_DATE_LANGUAGE | TO_CHAR 產出中文/英文月份不一致 | 顯式 'NLS_DATE_LANGUAGE=AMERICAN' 或 ALTER SESSION |
| TIMESTAMP 精度丟失 | DATE 只存秒,毫秒截斷 | 關鍵業務用 TIMESTAMP(6) |
| 夏令時缺口/重複 | 時區轉換出現 2:30 不存在或兩次 | 用 TIMESTAMP WITH TIME ZONE + 區域名 (Asia/Shanghai 無夏令時) |
總結
日期時間處理的核心原則:**顯式格式模型、顯式時區、優先用 TIMESTAMP WITH TIME ZONE、索引友好寫法(閉區間)、閏年/夏令時邊界測試**。掌握上述 27 函數與格式元素,覆蓋 99% 業務場景。