給定任一日期,需輸出該日期所在月份的所有日期,並按「週次」分組顯示每週的起始日 (週日) 與結束日 (週六),自動處理跨月週邊界 (首週可能包含上月末尾、末週可能包含下月初首)。
DECLARE @i INT; -- 迴圈計數器
DECLARE @curWk INT; -- 指定日期在該年的週數
DECLARE @fstWk INT; -- 該月 1 號在該年的週數
DECLARE @fstDay NVARCHAR(10); -- 該月第一天 (YYYY-MM-DD)
DECLARE @lstDay NVARCHAR(10); -- 該月最後一天
DECLARE @tmpDay SMALLDATETIME; -- 迴圈中當前週日日期
DECLARE @date SMALLDATETIME; -- 輸入指定日期
DECLARE @tmpDay1 SMALLDATETIME; -- 當週六日期
-- 測試輸入日期
SET @date = '2007-09-01';
-- 計算該月第一天字串
SELECT @fstDay = CONVERT(NVARCHAR(10),
CAST(CAST(YEAR(@date) AS NVARCHAR(4)) + '-' +
CAST(MONTH(@date) AS NVARCHAR(2)) + '-01' AS SMALLDATETIME), 120);
-- 取得該月第一天所屬週數
SET @fstWk = DATEPART(WK, @fstDay);
-- 計算該月最後一天
SET @lstDay = CONVERT(NVARCHAR(10), DATEADD(D, -1, DATEADD(M, 1, @fstDay)), 120);
-- ============================================================
-- 階段一:尋找本月第一個「完整週」的起始日 (週日)
-- 若 1 號非週日,則首週起始日為上個月的某個週日
-- ============================================================
SET @i = 0;
WHILE (@i < 8)
BEGIN
SET @tmpDay = DATEADD(DD, @i, @fstDay); -- 從 1 號往後推
SET @curWk = DATEPART(WK, @tmpDay);
IF @curWk > @fstWk -- 週數變大,表示進入下一週 (找到週日)
BEGIN
BREAK;
END
SET @i = @i + 1;
END
-- 此時 @tmpDay 為本月第二週的週日 (或第一週週日若 1 號即為週日)
-- ============================================================
-- 階段二:逐週輸出日期區間
-- ============================================================
SET @i = 1; -- 週次計數器
WHILE (@i < 7) -- 一月最多 6 週
BEGIN
IF @i = 1 -- 第一週特殊處理:從月首日到第一個週六
BEGIN
PRINT '第' + CAST(@i AS VARCHAR) + '週:' + @fstDay + '--------' + CONVERT(NVARCHAR(10), DATEADD(D, -1, @tmpDay), 120);
SET @i = @i + 1;
END
-- 若當前週日尚未超過月尾,繼續處理
IF DATEDIFF(D, @tmpDay, @lstDay) >= 0
BEGIN
SET @tmpDay1 = DATEADD(D, 6, @tmpDay); -- 週六 = 週日 + 6
-- 判斷週六是否超出月尾
IF DATEDIFF(D, @lstDay, @tmpDay1) > 0 -- 週六 > 月尾
BEGIN
PRINT '第' + CAST(@i AS VARCHAR) + '週:' + CONVERT(NVARCHAR(10), @tmpDay, 120) + '--------' + CONVERT(NVARCHAR(10), @lstDay, 120);
END
ELSE
BEGIN
PRINT '第' + CAST(@i AS VARCHAR) + '週:' + CONVERT(NVARCHAR(10), @tmpDay, 120) + '--------' + CONVERT(NVARCHAR(10), @tmpDay1, 120);
END
END
-- 移至下一週週日
SET @tmpDay = DATEADD(D, 7, @tmpDay);
-- 若下一週週日已超過月尾,結束迴圈
IF DATEDIFF(D, @tmpDay, @lstDay) < 0
BREAK;
SET @i = @i + 1;
END
第1週:2007-09-01--------2007-09-01
第2週:2007-09-02--------2007-09-08
第3週:2007-09-09--------2007-09-15
第4週:2007-09-16--------2007-09-22
第5週:2007-09-23--------2007-09-29
第6週:2007-09-30--------2007-09-30
sp_GetMonthlyWeeks @InputDate 供報表呼叫。DATEFIRST 設定或改用 DATEPART(WEEKDAY, ...) 邏輯。TRUNC(date, 'IW') 等)。