Calculating the next maintenance window in SCCM is a bit troublesome as you normally have to use WMI to get the data struc schedule data structure based on the current date/time.
CREATE
FUNCTION
[dbo].[SCCM_GetNextServiceWindow](@ScheduleToken AS
CHAR(16), @RecurrenceType
AS
INT) RETURNS
@NextServiceWindow TABLE
(ScheduleToken CHAR(16), RecurrenceType
INT, NextServiceWindow DATETIME, Duration
INT, IsGMTTime
BIT)
AS
BEGIN
--1 Occurs on 1/1/2012 12:00 AM 00011A8500080000
--2 Occurs every 3 day(s) effective 1/1/2012 8:00 PM 02811A8040100018
--3 Occurs every 3 week(s) on Saturday effective 1/1/2012 8:00 PM 02811A80401F6000
--3 Occurs every 1 week(s) on Saturday effective 1/1/2012 8:00 PM 02811A80401F2000
--5 Occurs day 2 of every 2 month(s) effective 1/1/2012 8:00 PM 02811A8040288800
--5 Occurs day 31 of every 1 month(s) effective 1/1/2012 8:00 PM 02811A80402FC400
--5 Occurs the last day of every 3 months effective 1/1/2012 8:00 PM 02811A8040280C00
--5 Occurs the last day of every 1 months effective 1/1/2012 8:00 PM 02811A8040280400
--4 Occurs the Third Monday of every 1 month(s) effective 1/1/2012 4:00 AM 00811A9E08221600
--4 Occurs the Last Wednesday of every 1 month(s) effective 1/1/2012 8:00 PM 02811A8040241000
--4 Occurs the Fourth Wednesday of every 1 month(s) effective 1/1/2012 8:00 PM 02811A8040241800
--4 Occurs the Last Monday of every 1 month(s) effective 1/1/2012 8:00 PM 02811A8040221000
--3 Occurs every 1 week(s) on Monday effective 1/1/2012 4:00 AM 00811A9E081A2000
DECLARE
@RecurrenceType_NONE INT
, @RecurrenceType_DAILY
INT
, @RecurrenceType_WEEKLY
INT
, @RecurrenceType_MONTHLYBYWEEKDAY
INT
, @RecurrenceType_MONTHLYBYDATE
INT
SELECT
@RecurrenceType_NONE = 1
, @RecurrenceType_DAILY = 2
, @RecurrenceType_WEEKLY = 3
, @RecurrenceType_MONTHLYBYWEEKDAY = 4
, @RecurrenceType_MONTHLYBYDATE = 5
--DECLARE @RecurrenceType INT; SET @RecurrenceTypeecurrenceType_WEEKLY = 3
, @RecurrenceType_MONTHLYBYWEEKDAY = 4
, @RecurrenceType_MONTHLYBYDATE = 5
--DECLARE = @RecurrenceType_WEEKLY
--DECLARE @ScheduleToken CHAR(16); SET @ScheduleToken = '00811A9E081A2000'
DECLARE
@ScheduleStartTime INT;
SET
@ScheduleStartTime = CAST(CONVERT(BINARY(4),
LEFT(@ScheduleToken, 8), 2)
AS
INT)
DECLARE
@ScheduleDuration INT;
SET
@ScheduleDuration = CAST(CONVERT(BINARY(4),
RIGHT(@ScheduleToken, 8), 2)
AS
INT)
-- Duration is in minutes
DECLARE
@Duration INT;
SET
@Duration = @ScheduleStartTime % POWER(2, 6)
-- Calculate the start time
DECLARE
@StartTime DATETIME; SET
@StartTime = CONVERT(DATETIME,
'01/01/1970 00:00:00')
SET
@StartTime = DATEADD(YEAR, (@ScheduleStartTime / POWER(2,6)) % POWER(2, 6), @StartTime)
SET
@StartTime = DATEADD(MONTH, ((@ScheduleStartTime / POWER(2,12)) % POWER(2, 4)) - 1, @StartTime)
SET
@StartTime = DATEADD(DAY, ((@ScheduleStartTime / POWER(2,16)) % POWER(2, 5)) - 1, @StartTime)
SET
@StartTime = DATEADD(HOUR, (@ScheduleStartTime / POWER(2,21)) % POWER(2, 5), @StartTime)
SET
@StartTime = DATEADD(MINUTE, (@ScheduleStartTime / POWER(2,26)) % POWER(2, 5), @StartTime)
-- Determinte UTC and Flags
DECLARE
@IsGMTTime BIT;
SET
@IsGMTTime = CAST(@ScheduleDuration % POWER(2, 1)
AS
BIT)
DECLARE
@Flags INT;
SET
@Flags = (@ScheduleDuration / POWER(2,19)) % POWER(2, 3)
-- Calculate the total duration in minutes
SET
@Duration = @Duration + ((@ScheduleDuration / POWER(2,22)) % POWER(2, 5)) * 24 * 60
-- DAYS
SET
@Duration = @Duration + ((@ScheduleDuration / POWER(2,27)) % POWER(2, 5)) * 60
-- HOURS
DECLARE
@Now DATETIME
IF @IsGMTTime = 1
BEGIN
SET
@Now = GETUTCDATE()
END
ELSE
BEGIN
SET
@Now = GETDATE()
END
DECLARE
@NextMaintenanceWindow DATETIME
IF @RecurrenceType = @RecurrenceType_NONE
BEGIN
IF DATEADD(MINUTE, @Duration, @StartTime) >
@Now BEGIN
SET
@NextMaintenanceWindow = @StartTime
END
END
ELSE
IF @RecurrenceType = @RecurrenceType_DAILY BEGIN
IF DATEADD(MINUTE, @Duration, @StartTime) >
@Now BEGIN
SET
@NextMaintenanceWindow = @StartTime
END
ELSE
BEGIN
-- Calculate the daily interval in minutes
DECLARE
@DailyInterval INT
SET
@DailyInterval = ((@ScheduleDuration / POWER(2,3)) % POWER(2, 5)) * 24 * 60
SET
@DailyInterval = @DailyInterval + ((@ScheduleDuration / POWER(2,8)) % POWER(2, 5)) * 60
SET
@DailyInterval = @DailyInterval + (@ScheduleDuration / POWER(2,13)) % POWER(2, 6)
-- Calculate the total number of completed intervals
DECLARE
@DailyNumberOfCompletedIntervals INT;
SET
@DailyNumberOfCompletedIntervals = ROUND(CAST(DATEDIFF(MINUTE, @StartTime, @Now)
AS
DECIMAL) / @DailyInterval, 0, 0)
-- Calculate the next interval
DECLARE
@DailyNextInterval DATETIME; SET
@DailyNextInterval = DATEADD(MINUTE, @DailyNumberOfCompletedIntervals * @DailyInterval, @StartTime)
-- Recalc the next interval if the next interval plus the expected duration is in the past
IF DATEADD(MINUTE, @Duration, @DailyNextInterval)
< @Now BEGIN
SET
@DailyNextInterval = DATEADD(MINUTE, (@DailyNumberOfCompletedIntervals + 1) * @DailyInterval, @StartTime)
END
SET
@NextMaintenanceWindow = @DailyNextInterval
END
END
ELSE
IF @RecurrenceType = @RecurrenceType_WEEKLY BEGIN
DECLARE
@WeeklyInterval INT;
SET
@WeeklyInterval = (@ScheduleDuration / POWER(2,13)) % POWER(2, 3)
DECLARE
@WeeklyDoW INT;
SET
@WeeklyDoW = (@ScheduleDuration / POWER(2,16)) % POWER(2, 3)
-- Adjust the start time to match the next day of week that matches the interval
DECLARE
@WeeklyStartTime DATETIME; SET
@WeeklyStartTime = DATEADD(DAY, (7 - DATEPART(WEEKDAY, @StartTime) + @WeeklyDoW % 7), @StartTime)
IF DATEADD(MINUTE, @Duration, @WeeklyStartTime)
> @Now BEGIN
SET
@NextMaintenanceWindow = @WeeklyStartTime
END
ELSE
BEGIN
-- Calculate the total number of completed intervals
DECLARE
@WeeklyNumberOfCompletedIntervals INT;
SET
@WeeklyNumberOfCompletedIntervals = ROUND(CAST(DATEDIFF(WEEK, @WeeklyStartTime, @Now)
AS
DECIMAL) / @WeeklyInterval, 0, 0)
-- Calculate the next interval
DECLARE
@WeeklyNextInterval DATETIME; SET
@WeeklyNextInterval = DATEADD(WEEK, @WeeklyNumberOfCompletedIntervals * @WeeklyInterval, @WeeklyStartTime)
-- Recalc the next interval if the next interval plus the expected duration is in the past
IF DATEADD(MINUTE, @Duration, @WeeklyNextInterval)
< @Now BEGIN
SET
@WeeklyNextInterval = DATEADD(WEEK, (@WeeklyNumberOfCompletedIntervals + 1) * @WeeklyInterval, @WeeklyStartTime)
END
SET
@NextMaintenanceWindow = @WeeklyNextInterval
END
END
ELSE
IF @RecurrenceType = @RecurrenceType_MONTHLYBYWEEKDAY BEGIN
DECLARE
@MonthlyBWWeek INT;
SET
@MonthlyBWWeek = (@ScheduleDuration / POWER(2,9)) % POWER(2, 3)
DECLARE
@MontlhyBWInterval INT;
SET
@MontlhyBWInterval = (@ScheduleDuration / POWER(2,12)) % POWER(2, 4)
DECLARE
@MonthlyBWDoW INT;
SET
@MonthlyBWDoW = (@ScheduleDuration / POWER(2,16)) % POWER(2, 3)
-- Calculate the total number of completed intervals
DECLARE
@MonthlyBWNumberOfCompletedIntervals INT;
SET
@MonthlyBWNumberOfCompletedIntervals = ROUND(CAST(DATEDIFF(MONTH, @StartTime, @Now)
AS
DECIMAL) / @MontlhyBWInterval, 0, 0)
IF @MonthlyBWWeek = 0
BEGIN
-- Calculate the next interval
DECLARE
@MonthlyBWLDOMNextInterval DATETIME; SET
@MonthlyBWLDOMNextInterval = DATEADD(MONTH, @MonthlyBWNumberOfCompletedIntervals * @MontlhyBWInterval, @StartTime)
-- Calculate last day of month
SET
@MonthlyBWLDOMNextInterval = DATEADD(DAY, DATEDIFF(DAY, @MonthlyBWLDOMNextInterval, DATEADD(DAY,
-1, DATEADD(M, DATEDIFF(MONTH, 0, @MonthlyBWLDOMNextInterval) + 1, 0))), @MonthlyBWLDOMNextInterval)
-- Calculate the last day of the week for the month
SET
@MonthlyBWLDOMNextInterval = DATEADD(DAY, -(7 - DATEPART(WEEKDAY, @MonthlyBWLDOMNextInterval) + @MonthlyBWDoW % 7), @MonthlyBWLDOMNextInterval)
IF DATEADD(MINUTE, @Duration, @MonthlyBWLDOMNextInterval)
< @Now BEGIN
-- Recalc for the next month interval
SET
@MonthlyBWLDOMNextInterval = DATEADD(MONTH, (@MonthlyBWNumberOfCompletedIntervals + 1) * @MontlhyBWInterval, @StartTime)
-- Calculate last day of month
SET
@MonthlyBWLDOMNextInterval = DATEADD(DAY, DATEDIFF(DAY, @MonthlyBWLDOMNextInterval, DATEADD(DAY,
-1, DATEADD(M, DATEDIFF(MONTH, 0, @MonthlyBWLDOMNextInterval) + 1, 0))), @MonthlyBWLDOMNextInterval)
-- Calculate the last day of the week for the month
SET
@MonthlyBWLDOMNextInterval = DATEADD(DAY, -(7 - DATEPART(WEEKDAY, @MonthlyBWLDOMNextInterval) + @MonthlyBWDoW % 7), @MonthlyBWLDOMNextInterval)
END
SET
@NextMaintenanceWindow = @MonthlyBWLDOMNextInterval
END
ELSE
BEGIN
-- Calculate the next interval
DECLARE
@MonthlyBWNextInterval DATETIME; SET
@MonthlyBWNextInterval = DATEADD(MONTH, @MonthlyBWNumberOfCompletedIntervals * @MontlhyBWInterval, @StartTime)
-- Set the date to the first day of the month
SET
@MonthlyBWNextInterval = DATEADD(DAY, -(DAY(@MonthlyBWNextInterval) - 1), @MonthlyBWNextInterval)
-- Set the date to the first day of week in the month
SET
@MonthlyBWNextInterval = DATEADD(DAY, (7 - DATEPART(WEEKDAY, @MonthlyBWNextInterval) + @MonthlyBWDoW) % 7, @MonthlyBWNextInterval)
-- Calculate date based on the week number to add
SET
@MonthlyBWNextInterval = DATEADD(WEEK, @MonthlyBWWeek-1, @MonthlyBWNextInterval)
IF DATEADD(MINUTE, @Duration, @MonthlyBWNextInterval)
< @Now BEGIN
-- Recalc for the next month interval
SET
@MonthlyBWNextInterval = DATEADD(MONTH, (@MonthlyBWNumberOfCompletedIntervals + 1) * @MontlhyBWInterval, @StartTime)
-- Set the date to the first day of the month
SET
@MonthlyBWNextInterval = DATEADD(DAY, -(DAY(@MonthlyBWNextInterval) - 1), @MonthlyBWNextInterval)
-- Set the date to the first day of week in the month
SET
@MonthlyBWNextInterval = DATEADD(DAY, (7 - DATEPART(WEEKDAY, @MonthlyBWNextInterval) + @MonthlyBWDoW % 7), @MonthlyBWNextInterval)
-- Calculate date based on the week number to add
SET
@MonthlyBWNextInterval = DATEADD(WEEK, @MonthlyBWWeek-1, @MonthlyBWNextInterval)
END
SET
@NextMaintenanceWindow = @MonthlyBWNextInterval
END
END
ELSE
IF @RecurrenceType = @RecurrenceType_MONTHLYBYDATE BEGIN
DECLARE
@MontlhyBDInterval INT;
SET
@MontlhyBDInterval = (@ScheduleDuration / POWER(2,10)) % POWER(2, 4)
DECLARE
@MonthlyBDDoM INT;
SET
@MonthlyBDDoM = (@ScheduleDuration / POWER(2,14)) % POWER(2, 5)
IF @MonthlyBDDoM = 0
BEGIN
@MontlhyBDInterval = (@ScheduleDuration / POWER(2,10)) % POWER(2, 4)
DECLARE
@MonthlyBDDoM INT;
SET
@MonthlyBDDoM = (@ScheduleDuration / POWER(2,14)) % POWER(2, 5)
IF @MonthlyBDDoM = 0
/* This
is
the last
day of
month logic */
-- Calculate the total number of completed intervals
DECLARE
@MonthlyBDLDOMNumberOfCompletedIntervals INT;
SET
@MonthlyBDLDOMNumberOfCompletedIntervals = ROUND(CAST(DATEDIFF(MONTH, @StartTime, @Now)
AS
DECIMAL) / @MontlhyBDInterval, 0, 0)
-- Calculate the next interval
DECLARE
@MonthlyBDLDOMNextInterval DATETIME; SET
@MonthlyBDLDOMNextInterval = DATEADD(MONTH, @MonthlyBDLDOMNumberOfCompletedIntervals * @MontlhyBDInterval, @StartTime)
-- Calculate last day of month
SET
@MonthlyBDLDOMNextInterval = DATEADD(DAY, DATEDIFF(DAY, @MonthlyBDLDOMNextInterval, DATEADD(DAY,
-1, DATEADD(M, DATEDIFF(MONTH, 0, @MonthlyBDLDOMNextInterval) + 1, 0))), @MonthlyBDLDOMNextInterval)
-- Recalc the next interval if the next interval plus the expected duration is in the past
IF DATEADD(MINUTE, @Duration, @MonthlyBDLDOMNextInterval)
< @Now BEGIN
SET
@MonthlyBDLDOMNextInterval = DATEADD(DAY, DATEDIFF(DAY, @MonthlyBDLDOMNextInterval, DATEADD(DAY,
-1, DATEADD(M, DATEDIFF(MONTH, 0, DATEADD(MONTH, (@MonthlyBDLDOMNumberOfCompletedIntervals + 1) * @MontlhyBDInterval,
@StartTime)) + 1, 0))), @MonthlyBDLDOMNextInterval)
END
SET
@NextMaintenanceWindow = @MonthlyBDLDOMNextInterval
END
ELSE
BEGIN
-- Check to make sure we won't loop forever if more than 31 days some how ends up in the token
IF @MonthlyBDDoM > 31
SET
@MonthlyBDDoM = 31
terval
END
-- Adjust the start time to match the next day of month that matches the interval
DECLARE
@MonthlyBDStartTime DATETIME; SET
@MonthlyBDStartTime = DATEADD(DAY, (31 - DATEPART(DAY, @StartTime) + @MonthlyBDDoM % 31), @StartTime)
-- This loop is used multiple times to search for the next valid date that falls on the desired day of month
WHILE(DATEPART(DAY, @MonthlyBDStartTime) <> @MonthlyBDDoM)
BEGIN
SET
@MonthlyBDStartTime = DATEADD(DAY, (31 - DATEPART(DAY, @MonthlyBDStartTime) + @MonthlyBDDoM)
% 31, @MonthlyBDStartTime)
END
IF DATEADD(MINUTE, @Duration, @MonthlyBDStartTime)
> @Now BEGIN
SET
@NextMaintenanceWindow = @MonthlyBDStartTime
END
ELSE
BEGIN
-- Calculate the total number of completed intervals
DECLARE
@MonthlyBDNumberOfCompletedIntervals INT;
SET
@MonthlyBDNumberOfCompletedIntervals = ROUND(CAST(DATEDIFF(MONTH, @MonthlyBDStartTime, @Now)
AS
DECIMAL) / @MontlhyBDInterval, 0, 0)
-- Calculate the next interval
DECLARE
@MonthlyBDNextInterval DATETIME; SET
@MonthlyBDNextInterval = DATEADD(MONTH, @MonthlyBDNumberOfCompletedIntervals * @MontlhyBDInterval, @MonthlyBDStartTime)
WHILE(DATEPART(DAY, @MonthlyBDNextInterval) <> @MonthlyBDDoM)
BEGIN
SET
@MonthlyBDNextInterval = DATEADD(DAY, (31 - DATEPART(DAY, @MonthlyBDNextInterval) + @MonthlyBDDoM
% 31), @MonthlyBDNextInterval)
END
-- Recalc the next interval if the next interval plus the expected duration is in the past
IF DATEADD(MINUTE, @Duration, @MonthlyBDNextInterval)
< @Now BEGIN
SET
@MonthlyBDNextInterval = DATEADD(MONTH, (@MonthlyBDNumberOfCompletedIntervals + 1) * @MontlhyBDInterval, @MonthlyBDNextInterval)
WHILE(DATEPART(DAY, @MonthlyBDNextInterval) <>
@MonthlyBDDoM) BEGIN
SET
@MonthlyBDNextInterval = DATEADD(DAY, (31 - DATEPART(DAY, @MonthlyBDNextInterval) + @MonthlyBDDoM
% 31), @MonthlyBDNextInterval)
END
, @MonthlyBDNextInterval) <>
@MonthlyBDDoM) BEGIN
SET
END
SET
@NextMaintenanceWindow = @MonthlyBDNextInterval
END
END
END
INSERT
INTO
@NextServiceWindow VALUES
(@ScheduleToken, @RecurrenceType, @NextMaintenanceWindow, @Duration, @IsGMTTime)
RETURN
END
To create a simple report that displays the client name and the next maintenance window use the following SQL for an SCCM report: