利用 `DATEDIFF` 計算基準日期 (1900-01-01) 到目前的時間間隔數,再用 `DATEADD` 加回去,即可對齊到該時間單位的起始點。再配合 `DATEADD(ms, -3, ...)` 取得上一個時間單位的結束瞬間 (精確到 3.33ms)。
| 需求 | T-SQL 範例 | 說明 |
|---|---|---|
| 本月第一天 00:00:00.000 | `DATEADD(mm, DATEDIFF(mm, 0, GETDATE()), 0)` | 對齊到月首 |
| 本周一 00:00:00.000 | `DATEADD(wk, DATEDIFF(wk, 0, GETDATE()), 0)` | 以 1900-01-01 (週一) 為基準 |
| 今年第一天 00:00:00.000 | `DATEADD(yy, DATEDIFF(yy, 0, GETDATE()), 0)` | 對齊到年首 |
| 本季第一天 00:00:00.000 | `DATEADD(qq, DATEDIFF(qq, 0, GETDATE()), 0)` | 對齊到季首 |
| 今天午夜 00:00:00.000 | `DATEADD(dd, DATEDIFF(dd, 0, GETDATE()), 0)` | 移除時間部分 |
| 上月最後瞬間 23:59:59.997 | `DATEADD(ms, -3, DATEADD(mm, DATEDIFF(mm, 0, GETDATE()), 0))` | 本月首減 3ms |
| 去年最後瞬間 | `DATEADD(ms, -3, DATEADD(yy, DATEDIFF(yy, 0, GETDATE()), 0))` | 今年首減 3ms |
| 本月最後瞬間 | `DATEADD(ms, -3, DATEADD(mm, DATEDIFF(mm, 0, GETDATE())+1, 0))` | 下月首減 3ms |
| 今年最後瞬間 | `DATEADD(ms, -3, DATEADD(yy, DATEDIFF(yy, 0, GETDATE())+1, 0))` | 明年首減 3ms |
| 本月第一個週一 | `DATEADD(wk, DATEDIFF(wk, 0, DATEADD(dd, 6-DATEPART(day, GETDATE()), GETDATE())), 0)` | 先算本月 1 號對應的週一 |
-- 本月完整資料
WHERE dt >= DATEADD(mm, DATEDIFF(mm, 0, GETDATE()), 0)
AND dt < DATEADD(mm, DATEDIFF(mm, 0, GETDATE())+1, 0);
使用 `< 下月首` 而非 `<= 本月末`,避免時間精度問題 (datetime 精度 3.33ms)。
DECLARE @StartOfMonth DATETIME = DATEADD(mm, DATEDIFF(mm, 0, GETDATE()), 0);
DECLARE @EndOfMonth DATETIME = DATEADD(ms, -3, DATEADD(mm, DATEDIFF(mm, 0, GETDATE())+1, 0));
掌握 `DATEDIFF`/`DATEADD` 組合模式,能靈活構建任意時間邊界,撰寫高效、無時區困擾的日期區間查詢。建議封裝為純量函數 (Scalar UDF) 或 CTE 共用,提升程式碼可讀性與維護性。