Search code examples
sqldate

Add business days to date in SQL without loops


I currently have a function in my SQL database that adds a certain amount of business days to a date, e.g. if you enter a date that is a Thursday and add two days, it will return the date of the following Monday. I'm not bothered about any holidays, only weekends are excluded.

The problem is that this is currently done using a while loop, and it appears to be massively slowing down the stored procedure that uses it while generating a table. Does anyone know if there is any way to perform this calculation without while loops or cursors?

Just for information, this is the current function:

ALTER FUNCTION [dbo].[AddWorkDaysToDate]
(   
@fromDate       datetime,
@daysToAdd      int
)
RETURNS datetime
AS
BEGIN   
DECLARE @toDate datetime
DECLARE @daysAdded integer

-- add the days, ignoring weekends (i.e. add working days)
set @daysAdded = 1
set @toDate = @fromDate

while @daysAdded <= @daysToAdd
begin
    -- add a day to the to date
    set @toDate = DateAdd(day, 1, @toDate)
    -- only move on a day if we've hit a week day
    if (DatePart(dw, @toDate) != 1) and (DatePart(dw, @toDate) != 7)
    begin
        set @daysAdded = @daysAdded + 1
    end
end

RETURN @toDate

END

Solution

  • This answer has been significantly altered since it was accepted, since the original was wrong. I'm more confident in the new query though, and it doesn't depend on DATEFIRST


    I think this should cover it:

    declare @fromDate datetime
    declare @daysToAdd int
    
    select @fromDate = '20130123',@DaysToAdd = 4
    
    declare @Saturday int
    select @Saturday = DATEPART(weekday,'20130126')
    
    ;with Numbers as (
        select 0 as n union all select 1 union all select 2 union all select 3 union all select 4
    ), Split as (
        select @DaysToAdd%5 as PartialDays,@DaysToAdd/5 as WeeksToAdd
    ), WeekendCheck as (
        select WeeksToAdd,PartialDays,MAX(CASE WHEN DATEPART(weekday,DATEADD(day,n.n,@fromDate))=@Saturday THEN 1 ELSE 0 END) as HitWeekend
        from
        Split t
            left join
        Numbers n
            on
                t.PartialDays >= n.n
    group by WeeksToAdd,PartialDays
    )
    select DATEADD(day,WeeksToAdd*7+PartialDays+CASE WHEN HitWeekend=1 THEN 2 ELSE 0 END,@fromDate)
    from WeekendCheck
    

    We split the time to be added into a number of weeks and a number of days within a week. We then use a small numbers table to work out if adding those few days will result in us hitting a Saturday. If it does, then we need to add 2 more days onto the total.