Our solution to this was to implement a schedule table which held a RunDate and a TradeDate, include a task which checked to see if the current day was a valid day and then run the package daily. Nice and simple. The final step was to populate the schedule table with data for the next year and this is where I had to address the T-SQL challenge.
Fortunately, there are many solutions on the web for this sort of thing, but i'm going to post my objects here for reference.
NB: I like to keep all these type of generic functions in a Utils schema so that they can managed in a database project and deployed to a database as required.
CREATE FUNCTION Utils.IsWeekDay(@Dt DATE)
RETURNS BIT
AS
BEGIN
DECLARE @IsWeekDay BIT = 1
IF DATENAME(DW,@Dt) IN ('SATURDAY', 'SUNDAY')
SET @IsWeekDay = 0
RETURN @IsWeekDay
END
GO
CREATE FUNCTION Utils.GetNextWeekDay(@Dt DATE)
RETURNS DATE
AS
BEGIN
DECLARE @NxtDt DATE
SET @NxtDt = DATEADD(D,1,@Dt)
IF (SELECT Utils.IsWeekDay(@NxtDt)) != 1
BEGIN
SET @NxtDt = DATEADD(D,1,@NxtDt)
IF (SELECT Utils.IsWeekDay(@NxtDt)) != 1
SET @NxtDt = DATEADD(D,1,@NxtDt)
END
RETURN @NxtDt
END
GO
CREATE FUNCTION Utils.GetPreviousWeekDay(@Dt DATETIME)
RETURNS DATETIME
AS
BEGIN
DECLARE @NxtDt DATE
SET @NxtDt = DATEADD(D,-1,@Dt)
IF (SELECT Utils.IsWeekDay(@NxtDt)) != 1
BEGIN
SET @NxtDt = DATEADD(D,-1,@NxtDt)
IF (SELECT Utils.IsWeekDay(@NxtDt)) != 1
SET @NxtDt = DATEADD(D,-1,@NxtDt)
END
RETURN @NxtDt
END
GO
-- and this is how we use the functions to get the schedule table for 2011
DECLARE @Year CHAR(8) = '20110101';
WITH Months
AS
(SELECT 0 AS Mth, CONVERT(DATETIME, @Year) AS Dt
UNION ALL
SELECT 1 + Mth, DATEADD(M, 1+Mth, @Year) AS Dt
FROM Months
WHERE Mth < 11
)
SELECT CASE Utils.IsWeekDay(Dt) WHEN 1 THEN Dt ELSE Utils.GetNextWeekDay(Dt) END AS RunDate,
Utils.GetPreviousWeekDay(Dt) AS TradeDate
FROM Months
GO