核心公式:DATEADD(單位, DATEDIFF(單位, 0, 參考日期), 0) —— 先算「距基準日 1900-01-01 有多少個單位」,再加回去,得到該單位的「起始點」。
| 需求 | T-SQL 表達式 | 說明 |
|---|---|---|
| 本月第一天 00:00 | DATEADD(month, DATEDIFF(month, 0, GETDATE()), 0) | 月份邊界 |
| 本週一 00:00 | DATEADD(week, DATEDIFF(week, 0, GETDATE()), 0) | 以週一為週首(依 @@DATEFIRST) |
| 今年第一天 00:00 | DATEADD(year, DATEDIFF(year, 0, GETDATE()), 0) | 年份邊界 |
| 本季第一天 00:00 | DATEADD(quarter, DATEDIFF(quarter, 0, GETDATE()), 0) | 季度邊界 |
| 今天 00:00 | DATEADD(day, DATEDIFF(day, 0, GETDATE()), 0) | 日期邊界 |
| 上月最後一天 23:59:59.997 | DATEADD(ms, -3, DATEADD(month, DATEDIFF(month, 0, GETDATE()), 0)) | 減 3 ms 避開午夜 |
| 去年最後一天 23:59:59.997 | DATEADD(ms, -3, DATEADD(year, DATEDIFF(year, 0, GETDATE()), 0)) | 同上 |
| 本月最後一天 23:59:59.997 | DATEADD(ms, -3, DATEADD(month, DATEDIFF(month, 0, GETDATE())+1, 0)) | 下月首日減 3 ms |
| 今年最後一天 23:59:59.997 | DATEADD(ms, -3, DATEADD(year, DATEDIFF(year, 0, GETDATE())+1, 0)) | 明年首日減 3 ms |
| 本月第一個週一 | DATEADD(week, DATEDIFF(week, 0, DATEADD(day, 6-DATEPART(day, GETDATE()), GETDATE())), 0) | 先算本月 1 號屬第幾週 |
GETDATE():傳回 datetime,精度 3.33 ms,預設值常用於 DEFAULT (GETDATE())。SYSDATETIME():datetime2(7),100 ns 精度(SQL 2008+)。SYSUTCDATETIME():UTC 高精度時間。-- CONVERT 風格代碼(常用)
SELECT CONVERT(VARCHAR(30), GETDATE(), 120); -- 2024-01-15 14:30:45 (ODBC 標準)
SELECT CONVERT(VARCHAR(30), GETDATE(), 111); -- 2024/01/15 (日本/ISO 短日期)
SELECT CONVERT(VARCHAR(30), GETDATE(), 106); -- 15 Jan 2024 (歐洲風格)
SELECT CONVERT(VARCHAR(30), GETDATE(), 9); -- Jan 15 2024 2:30:45:123PM (含毫秒)
-- FORMAT (SQL 2012+,.NET 格式字串,效能較慢但極靈活)
SELECT FORMAT(GETDATE(), 'yyyy-MM-dd HH:mm:ss.fff', 'zh-TW'); -- 2024-01-15 14:30:45.123
SELECT FORMAT(GETDATE(), 'D', 'zh-TW'); -- 2024年1月15日
| 日期部分 | 縮寫 | 範圍 | 範例 |
|---|---|---|---|
| 年 | year, yy, yyyy | 1753-9999 | DATEPART(year, GETDATE()) → 2024 |
| 季 | quarter, qq, q | 1-4 | DATEPART(qq, GETDATE()) → 1 |
| 月 | month, mm, m | 1-12 | DATENAME(month, GETDATE()) → 一月 |
| 日 | day, dd, d | 1-31 | DATEPART(dd, GETDATE()) → 15 |
| 年內天數 | dayofyear, dy, y | 1-366 | |
| 週 | week, wk, ww | 1-53 | |
| 星期幾 | weekday, dw, w | 1-7 (週日-週六) | DATENAME(dw, GETDATE()) → 星期一 |
| 時 | hour, hh | 0-23 | |
| 分 | minute, mi, n | 0-59 | |
| 秒 | second, ss, s | 0-59 | |
| 毫秒 | millisecond, ms | 0-999 |
DATEPART 回傳 int,適合比較/運算;DATENAME 回傳 nvarchar,適合顯示。
錯誤寫法: WHERE EntryDate = '2024-01-15' → 只會比中 2024-01-15 00:00:00.000。
正確寫法 1(範圍):
WHERE EntryDate >= '2024-01-15' AND EntryDate < '2024-01-16'
正確寫法 2(LIKE 配合字串轉換):
WHERE CONVERT(VARCHAR(10), EntryDate, 120) = '2024-01-15'
-- 或
WHERE EntryDate LIKE '2024-01-15%'
建議採用「範圍法」,可利用索引 Seek,LIKE 會導致掃描。
-- 計算某筆記錄距今幾個小時
SELECT DATEDIFF(hour, EntryDate, GETDATE()) AS HoursAgo FROM WebLog;
-- 計算會員註冊一個月後到期日
SELECT DATEADD(month, 1, FirstVisitDate) AS ExpireDate FROM Members;
-- 注意:DATEADD(month,1) 只是月份 +1,不保證 30 天(1/31 → 2/28/29)
CONVERT(datetime, '2024-01-15', 120) 或 PARSE('15/01/2024' AS datetime USING 'zh-TW'),避免隱性轉換受 SET DATEFORMAT/SET LANGUAGE 影響。datetimeoffset + AT TIME ZONE(SQL 2016+)處理時區轉換。WHERE YEAR(CreateDate) = 2024),改寫為範圍查詢以利索引。DATEADD/DATEDIFF 以此為原點,負數代表 1900 年前(不支援)。DECLARE @Now DATETIME = GETDATE();
SELECT
DATEADD(month, DATEDIFF(month, 0, @Now), 0) AS MonthStart,
DATEADD(month, DATEDIFF(month, 0, @Now)+1, 0) AS NextMonthStart,
DATEADD(day, DATEDIFF(day, 0, @Now), 0) AS DayStart,
DATEADD(day, DATEDIFF(day, 0, @Now)+1, 0) AS NextDayStart,
DATEADD(year, DATEDIFF(year, 0, @Now), 0) AS YearStart;
掌握這套日期時間工具箱,即能輕鬆應對報表統計、排程觸發、資料歸檔等各類時間維度業務需求。