DECLARE @dt DATETIME = GETDATE();
-- 短日期 YYYY-M-D
SELECT REPLACE(CONVERT(VARCHAR(10), @dt, 120), '-0', '-');
-- 長日期 YYYY年MM月DD日 (方法 1: STUFF)
SELECT STUFF(STUFF(CONVERT(CHAR(8), @dt, 112), 5, 0, '年'), 8, 0, '月') + '日';
-- 長日期 YYYY年MM月DD日 (方法 2: DATENAME)
SELECT DATENAME(year, @dt) + '年' + DATENAME(month, @dt) + '月' + DATENAME(day, @dt) + '日';
-- 長日期 YYYY年M月D日 (無前導零)
SELECT DATENAME(year, @dt) + '年' + CAST(DATEPART(month, @dt) AS VARCHAR) + '月' + DATENAME(day, @dt) + '日';
-- 完整日期時間 YYYY-MM-DD HH:MI:SS.mmm
SELECT CONVERT(CHAR(10), @dt, 120) + ' ' + CONVERT(CHAR(12), @dt, 114);
DECLARE @dt DATETIME = GETDATE(), @n INT = 3;
-- 1. 年初 / 年尾
SELECT DATEADD(year, DATEDIFF(year, 0, @dt), 0) AS YearStart;
SELECT DATEADD(day, -1, DATEADD(year, DATEDIFF(year, 0, @dt) + 1, 0)) AS YearEnd;
-- 2. 季初 / 季尾
DECLARE @q INT = DATEPART(quarter, @dt);
SELECT DATEADD(quarter, DATEDIFF(quarter, 0, @dt), 0) AS QuarterStart;
SELECT DATEADD(day, -1, DATEADD(quarter, DATEDIFF(quarter, 0, @dt) + 1, 0)) AS QuarterEnd;
-- 3. 月初 / 月尾 (最穩健)
SELECT DATEADD(month, DATEDIFF(month, 0, @dt), 0) AS MonthStart;
SELECT DATEADD(day, -1, DATEADD(month, DATEDIFF(month, 0, @dt) + 1, 0)) AS MonthEnd;
-- ❌ 錯誤寫法:DATEADD(month, 1, DATEADD(day, -DAY(@dt), @dt)) -- 2/28/2023 → 3/28/2023 (非月尾)
-- 4. 週任意一天 (以週日為第 1 天)
SELECT DATEADD(day, @n - DATEPART(weekday, @dt), @dt);
-- 5. 週任意一天 (指定週一為第 1 天)
SELECT DATEADD(day, @n - (DATEPART(weekday, @dt) + @@DATEFIRST - 2) % 7 - 1, @dt);
| 需求 | SQL 片段 |
|---|---|
| 年齡計算 | DATEDIFF(year, @Birth, GETDATE()) - CASE WHEN DATEADD(year, DATEDIFF(year, @Birth, GETDATE()), @Birth) > GETDATE() THEN 1 ELSE 0 END |
| 本月工作日數 | 見下方函數 |
| 兩日期相差月數 | DATEDIFF(month, @Start, @End) - CASE WHEN DAY(@Start) > DAY(@End) THEN 1 ELSE 0 END |
| 下一個工作日 | 遞迴 CTE 或迴圈跳過週末 |
| 季度標籤 | 'Q' + CAST(DATEPART(quarter, @dt) AS VARCHAR) + ' ' + CAST(YEAR(@dt) AS VARCHAR) |
CREATE FUNCTION dbo.fn_WorkDays(@Start DATE, @End DATE)
RETURNS INT
AS BEGIN
DECLARE @Days INT = DATEDIFF(day, @Start, @End) + 1;
DECLARE @Weeks INT = @Days / 7;
DECLARE @Remain INT = @Days % 7;
DECLARE @StartDow INT = DATEPART(weekday, @Start); -- 受 @@DATEFIRST 影響
RETURN @Weeks * 5 +
CASE
WHEN @StartDow + @Remain - 1 <= 5 THEN @Remain
WHEN @StartDow <= 5 THEN 6 - @StartDow
ELSE 0
END;
END;
DATE:3 bytes,純日期,無時區困擾。TIME:100ns 精度,純時間。DATETIME2:取代 DATETIME,範圍更大、精度更高、儲存更小 (6-8 bytes)。DATETIMEOFFSET:含時區位移,全球化系統必備。EOMONTH(@dt) (2012+):直接取得月尾日期。DATEFROMPARTS(y, m, d) (2012+):組合日期無字串轉換風險。日期處理核心原則:儲存用標準型別 (DATETIME2/DATE)、運算用 DATEDIFF/DATEADD 邊界法、輸出用 CONVERT 格式化、避免字串拼接運算。將常用邊界推算封裝為內聯函數/視圖,全專案統一呼叫,消除時區、語言、閏年等邊界 Bug。