DB2 對日期時間的支援極為直觀強大,初學者常驚喜於其簡潔語法。本文系統整理日期時間註冊器、擷取函數、運算技巧、格式轉換、自訂格式化函數等核心知識,並附上常見陷阱與內建函數速查表。
-- 取得目前日期、時間、時間戳
SELECT current date FROM sysibm.sysdummy1;
SELECT current time FROM sysibm.sysdummy1;
SELECT current timestamp FROM sysibm.sysdummy1;
-- CLP 中可直接用 VALUES
VALUES current date;
VALUES current time;
VALUES current timestamp;
sysibm.sysdummy1 是特殊記憶體表,用於查詢註冊器值。
-- 轉換為 GMT/UTC
current time - current timezone;
current timestamp - current timezone;
-- 擷取年月日時分秒微秒
YEAR(current timestamp);
MONTH(current timestamp);
DAY(current timestamp);
HOUR(current timestamp);
MINUTE(current timestamp);
SECOND(current timestamp);
MICROSECOND(current timestamp);
-- 從時間戳擷取日期/時間
DATE(current timestamp);
TIME(current timestamp);
current date + 1 YEAR;
current date + 3 YEARS + 2 MONTHS + 15 DAYS;
current time + 5 HOURS - 3 MINUTES + 10 SECONDS;
-- 兩日期相差天數
days(current date) - days(date('1999-10-22'));
-- 將時間戳微秒歸零
CURRENT TIMESTAMP - MICROSECOND(current timestamp) MICROSECONDS;
-- 日期時間轉字串
char(current date);
char(current time);
char(current date + 12 hours);
-- 字串轉日期時間(支援多種格式)
TIMESTAMP('2002-10-20-12.00.00.000000');
TIMESTAMP('2002-10-20 12:00:00');
DATE('2002-10-20');
DATE('10/20/2002');
TIME('12:00:00');
TIME('12.00.00');
SELECT DATE(2001-09-22) FROM SYSIBM.SYSDUMMY1;
-- 結果:05/24/0006 (相差近 2000 年!)
原因:無引號時參數被視為數值運算 (2001-9)-22 = 1970,DATE(1970) 解釋為距公元元年 1970 天。日期字串務必加引號!
TIMESTAMPDIFF(n, char(ts1-ts2)) 回傳近似差值(不考慮閏年、假設每月 30 天)。n 對應單位:1=秒微秒、2=秒、4=分、8=時、16=天、32=週、64=月、128=季、256=年。
精確秒差計算:
(DAYS(t1) - DAYS(t2)) * 86400 + (MIDNIGHT_SECONDS(t1) - MIDNIGHT_SECONDS(t2))
建議封裝為 UDF:
CREATE FUNCTION secondsdiff(t1 TIMESTAMP, t2 TIMESTAMP)
RETURNS INT
RETURN (DAYS(t1)-DAYS(t2))*86400 + (MIDNIGHT_SECONDS(t1)-MIDNIGHT_SECONDS(t2))@
CREATE FUNCTION daysinyear(yr INT)
RETURNS INT
RETURN (CASE MOD(yr,400) WHEN 0 THEN 366 ELSE
CASE MOD(yr,4) WHEN 0 THEN
CASE MOD(yr,100) WHEN 0 THEN 365 ELSE 366 END
ELSE 365 END END)@
| 函數 | 說明 |
|---|---|
| DAYNAME | 回傳星期名稱(如 Friday) |
| DAYOFWEEK | 星期幾(1=星期日) |
| DAYOFWEEK_ISO | ISO 星期幾(1=星期一) |
| DAYOFYEAR | 一年中的第幾天(1-366) |
| DAYS | 日期整數表示 |
| JULIAN_DAY | 儒略日數(自公元前 4712-01-01) |
| MIDNIGHT_SECONDS | 午夜至指定時間的秒數(0-86400) |
| MONTHNAME | 月份名稱(如 January) |
| TIMESTAMP_ISO | 依日期/時間/時間戳回傳時間戳 |
| TIMESTAMP_FORMAT | 依字元樣板解析字串為時間戳 |
| TIMESTAMPDIFF | 兩時間戳近似差值 |
| TO_CHAR / VARCHAR_FORMAT | 依樣板格式化時間戳為字串 |
| TO_DATE / TIMESTAMP_FORMAT | 依樣板解析字串為時間戳 |
| WEEK | 一年中的第幾週(1-54,週日為首) |
| WEEK_ISO | ISO 週數(1-53,週一為首) |
預設格式由建庫時的 territory 決定。可透過重新綁定工具包改變:
cd sqllib\bnd
db2 connect to DBNAME
db2 bind @db2ubind.lst datetime ISO blocking all grant public
支援格式:DEF(地區預設)、EUR、ISO(YYYY-MM-DD)、JIS、LOC、USA。
若需 yyyymmdd 等非標準格式,可自寫 UDF:
CREATE FUNCTION ts_fmt(TS TIMESTAMP, fmt VARCHAR(20))
RETURNS VARCHAR(50)
RETURN
WITH tmp (dd,mm,yyyy,hh,mi,ss,nnnnnn) AS (
SELECT SUBSTR(DIGITS(DAY(TS)),9),
SUBSTR(DIGITS(MONTH(TS)),9),
RTRIM(CHAR(YEAR(TS))),
SUBSTR(DIGITS(HOUR(TS)),9),
SUBSTR(DIGITS(MINUTE(TS)),9),
SUBSTR(DIGITS(SECOND(TS)),9),
RTRIM(CHAR(MICROSECOND(TS)))
FROM sysibm.sysdummy1
)
SELECT CASE fmt
WHEN 'yyyymmdd' THEN yyyy||mm||dd
WHEN 'mm/dd/yyyy' THEN mm||'/'||dd||'/'||yyyy
WHEN 'yyyy/dd/mm hh:mi:ss' THEN yyyy||'/'||mm||'/'||dd||' '||hh||':'||mi||':'||ss
WHEN 'nnnnnn' THEN nnnnnn
ELSE 'date format '||COALESCE(fmt,'')||' not recognized.'
END FROM tmp@
使用範例:
VALUES ts_fmt(current timestamp,'yyyymmdd'); -- '20030818'
VALUES ts_fmt(current timestamp,'asa'); -- 'date format asa not recognized.'
DB2 日期時間功能豐富且易用,掌握註冊器、擷取函數、自然語言運算、格式轉換與自訂 UDF,即能應對絕大多數業務場景。記住:字串字面值務必加引號,避免隱性數值運算導致驚人錯誤。