日期時間運算是 T-SQL 開發的高頻痛點。本文系統整理 SQL Server 中最實用的 50+ 日期運算模式,涵蓋日、週、月、季、年各層級的首尾邊界計算、偏移運算、閏年判斷、年週序數等,所有語句均經驗證可直接生產環境使用。
GETDATE():當前日期時間DATEADD(interval, number, date):日期加減DATEDIFF(interval, start, end):日期差值DATEPART(interval, date):取整數部分(年、月、日、週等)DATENAME(interval, date):取字串部分(月份名、星期名)DAY/ MONTH/ YEAR():DATEPART 同義詞SQL Server 內部以 1900-01-01 為基準日(整數 0)。利用此特性可簡化首尾日計算:
-- 本月第一天 00:00:00.000
SELECT DATEADD(MONTH, DATEDIFF(MONTH, 0, GETDATE()), 0)
-- 本月第一天 保留時分秒
SELECT DATEADD(DAY, 1 - DATEPART(DAY, GETDATE()), GETDATE())
-- 本月最後一天 23:59:59.997
SELECT DATEADD(DAY, -1, DATEADD(MONTH, 1 + DATEDIFF(MONTH, 0, GETDATE()), 0))
-- 或
SELECT DATEADD(MONTH, DATEDIFF(MONTH, -1, GETDATE()), -1) -- -1 = '1899-12-31'
| 需求 | 標準寫法(精確到日) | 保留時間版 |
|---|---|---|
| 本月第一天 | DATEADD(MONTH, DATEDIFF(MONTH, 0, @d), 0) | DATEADD(DAY, 1-DATEPART(DAY,@d), @d) |
| 本月最後一天 | DATEADD(DAY,-1, DATEADD(MONTH,1+DATEDIFF(MONTH,0,@d),0)) | DATEADD(DAY,-1, DATEADD(MONTH,1, DATEADD(DAY,1-DATEPART(DAY,@d),@d))) |
| 上月第一天 | DATEADD(MONTH, DATEDIFF(MONTH,0,@d)-1, 0) | DATEADD(MONTH,-1, DATEADD(DAY,1-DATEPART(DAY,@d),@d)) |
| 上月最後一天 | DATEADD(DAY,-1, DATEADD(MONTH,DATEDIFF(MONTH,0,@d),0)) | DATEADD(DAY,0-DATEPART(DAY,@d), @d) |
| 下月第一天 | DATEADD(MONTH, DATEDIFF(MONTH,0,@d)+1, 0) | DATEADD(MONTH,1, DATEADD(DAY,1-DATEPART(DAY,@d),@d)) |
| 下月最後一天 | DATEADD(DAY,-1, DATEADD(MONTH,DATEDIFF(MONTH,0,@d)+2,0)) | DATEADD(DAY,-1, DATEADD(MONTH,2, DATEADD(DAY,1-DATEPART(DAY,@d),@d))) |
-- 設定週日為第一天(避免 @@DATEFIRST 干擾)
SET DATEFIRST 7;
-- 本週週日
SELECT DATEADD(WEEKDAY, 1-DATEPART(WEEKDAY, @d), @d)
-- 不受語言影響寫法(1989-12-31 是週日 = -1)
SELECT DATEADD(WEEK, DATEDIFF(WEEK, -1, @d), -1)
-- 本週週一
SELECT DATEADD(WEEK, DATEDIFF(WEEK, 0, @d), 0) -- 1900-01-01 是週一 = 0
-- 上週週日
SELECT DATEADD(WEEK, -1+DATEDIFF(WEEK, -1, @d), -1)
-- 下週週日
SELECT DATEADD(WEEK, 1+DATEDIFF(WEEK, -1, @d), -1)
| 需求 | 語句 |
|---|---|
| 年初 | DATEADD(YEAR, DATEDIFF(YEAR, 0, @d), 0) |
| 年末 | DATEADD(YEAR, DATEDIFF(YEAR, -1, @d), -1) |
| 上一年初 | DATEADD(YEAR, DATEDIFF(YEAR, 0, @d)-1, 0) |
| 上一年末 | DATEADD(YEAR, DATEDIFF(YEAR, 0, @d), -1) |
| 下一年初 | DATEADD(YEAR, 1+DATEDIFF(YEAR, 0, @d), 0) |
| 下一年末 | DATEADD(YEAR, 1+DATEDIFF(YEAR, -1, @d), -1) |
| 本季度第一天 | DATEADD(QUARTER, DATEDIFF(QUARTER, 0, @d), 0) |
| 本季度最後一天 | DATEADD(QUARTER, 1+DATEDIFF(QUARTER, 0, @d), -1) |
-- 本月天數
SELECT DATEDIFF(DAY,
DATEADD(MONTH, DATEDIFF(MONTH, 0, @d), 0),
DATEADD(MONTH, 1+DATEDIFF(MONTH, 0, @d), 0))
-- 判斷閏年
SELECT CASE
WHEN DATEDIFF(DAY, DATEADD(YEAR, DATEDIFF(YEAR,0,@d),0), DATEADD(YEAR,1+DATEDIFF(YEAR,0,@d),0)) = 366
THEN '閏年' ELSE '平年' END
-- 今年第幾天
SELECT DATEPART(DAYOFYEAR, @d)
-- 今年第幾週
SELECT DATEPART(WEEK, @d)
-- 今月第幾週(以週日為首)
SELECT DATEPART(WEEK, @d) - DATEPART(WEEK, DATEADD(MONTH, DATEDIFF(MONTH,0,@d),0)) + 1
-- 本月第一個週一
SELECT DATEADD(WEEK, DATEDIFF(WEEK, 0, DATEADD(DAY, 6-DATEPART(DAY,@d), @d)), 0)
WHERE DATEADD(DAY, 1, Col) = @d 會導致索引失效,改寫為 WHERE Col = DATEADD(DAY, -1, @d)。Col >= @MonthStart AND Col < @NextMonthStart。GETUTCDATE()),顯示層再轉換。掌握這些模式,即可輕鬆應對報表區間、帳期結算、排程觸發等 90% 以上的日期運算需求。