add business days to a date

  • Hello,

    Is there a way to add a number of Business Days (not including Saturday and Sunday) to a date?

    Users fill out a job request form. The Requested Date is just captured by GetDate(). I would like to add a Due Date that is X Business Days from the Requested Date.

    Thanks,

    Marc

  • There is no built-in method to handle this. Something like this would probably work, but would not handle holidays. The best way would use a dates table.

    [font="Courier New"]-- populate temp numbers table

    SELECT TOP 10

       IDENTITY(INT, 1,1) AS n

    INTO

       #nums

    FROM

       sys.all_objects

    SELECT

       MIN(DATEADD(DAY, n, GETDATE()))

    FROM

       #nums

    WHERE

       n BETWEEN 5 AND 7 AND

       CASE

           WHEN DATENAME(dw, DATEADD(DAY, n, GETDATE())) IN ('Saturday', 'Sunday') THEN 0

           ELSE 1

       END = 1

    DROP TABLE #nums

    [/font]

  • Thanks Jack!

    This will work perfectly for me.

    Thanks again.

    Marc

  • Jack Corbett (12/23/2008)


    There is no built-in method to handle this. Something like this would probably work, but would not handle holidays. The best way would use a dates table.

    [font="Courier New"]-- populate temp numbers table

    SELECT TOP 10

       IDENTITY(INT, 1,1) AS n

    INTO

       #nums

    FROM

       sys.all_objects

    SELECT

       MIN(DATEADD(DAY, n, GETDATE()))

    FROM

       #nums

    WHERE

       n BETWEEN 5 AND 7 AND

       CASE

           WHEN DATENAME(dw, DATEADD(DAY, n, GETDATE())) IN ('Saturday', 'Sunday') THEN 0

           ELSE 1

       END = 1

    DROP TABLE #nums

    [/font]

    Sneaky... 😀

    --Jeff Moden


    RBAR is pronounced "ree-bar" and is a "Modenism" for Row-By-Agonizing-Row.
    First step towards the paradigm shift of writing Set Based code:
    ________Stop thinking about what you want to do to a ROW... think, instead, of what you want to do to a COLUMN.

    Change is inevitable... Change for the better is not.


    Helpful Links:
    How to post code problems
    How to Post Performance Problems
    Create a Tally Function (fnTally)

  • Well, now that I play with this, it's not doing what I need. Or, maybe I don't understand the code properly.

    Is the line "n BETWEEN 5 AND 7" supposed to give me 5 Business Days? If so, that should be December 30th. But running the code gives me a result of Dec 29th.

    Changing it to read "n BETWEEN 4 AND 6" or "n BETWEEN 6 AND 8" yields the same result.

    Sorry for the ignorance!

    Marc

  • Okay the issue with my code is that I am including Requested Date as one of the business days. The Between 5 and 7 basically says I need to go out 5 to 7 days to the first day that is not on a weekend, since there are 2 non-business days in a 7 day week.

    By not including the Requested Date in the business days you basically just need to add 7 to any weekday date since you will always hit the weekend as part of the 5 days, add 6 for Saturday, and 5 for Sunday.

    It obviously gets more complex if you want to count holidays as non-business days and you need a dates table to know when they are anyway.

  • Here is a small little modification to Jack's code which will give you what you want.

    -- populate temp numbers table

    SELECT TOP 100

    IDENTITY(INT, 1,1) AS n

    INTO

    #nums

    FROM

    sys.all_objects;

    ;WITH dates (n, datecolumn)

    AS (SELECT ROW_NUMBER() OVER(ORDER BY n)

    ,DATEADD(DAY, n, GETDATE())

    FROM #nums

    WHERE 1 = CASE WHEN DATENAME(dw, DATEADD(DAY, n, GETDATE())) IN ('Saturday', 'Sunday')

    THEN 0

    ELSE 1

    END)

    SELECT datecolumn

    FROM dates

    WHERE n = 10; -- date 10 business days in future

    DROP TABLE #nums;

    Jeffrey Williams
    “We are all faced with a series of great opportunities brilliantly disguised as impossible situations.”

    ― Charles R. Swindoll

    How to post questions to get better answers faster
    Managing Transaction Logs

  • Thanks Jack and Jeffrey for all your help!

    I believe this is what I need.

    Happy Holidays,

    Marc

  • And here is the above cleaned up a little:

    Declare @days int;

    Declare @inputDate datetime;

    Set @days = 10;

    Set @inputDate = '20081225';

    ;With nums(n)

    As (Select Top(@days + (@days / 5 + 1) * 2) -- make sure we have enough days

    row_number() over(Order By ao.[name]) As n

    From sys.all_objects ao

    )

    ,dates(rk ,datecolumn)

    As (Select row_number() over(Order By n)

    ,dateadd(day, n, @inputDate)

    From nums

    Where datename(dw, dateadd(day, n, @inputDate)) Not In ('Saturday', 'Sunday')

    )

    Select datecolumn

    From dates

    Where rk = @days; -- date 10 business days in future

    Removed the temp table and moved it into a CTE (which can easily be replaced by a numbers table). Also removed the case statement in the where clause because it really wasn't needed. Added the calculation to the TOP so we only return as many rows as are needed - well, maybe a couple extra rows 😉

    Jeffrey Williams
    “We are all faced with a series of great opportunities brilliantly disguised as impossible situations.”

    ― Charles R. Swindoll

    How to post questions to get better answers faster
    Managing Transaction Logs

  • Heh... dunno why we're pussy footin' around it... :hehe: if you don't already have a Tally table, now would be a good time to build one. To find out how to build one and how it can be used instead of loops, please see the following article.

    [font="Arial Black"]The "Numbers" or "Tally" Table: What it is and how it replaces a loop.[/font][/url]

    Then, the code becomes even simpler...

    DECLARE @SomeDate DATETIME,

    @BusinessDays INT

    SELECT @SomeDate = GETDATE(),

    @BusinessDays = 5

    ;WITH

    cteAddBusinessDay AS

    (

    SELECT t.N+@SomeDate AS SomeDate,

    ROW_NUMBER() OVER (ORDER BY t.N+@SomeDate) AS Businessday

    FROM dbo.Tally t

    WHERE t.N <= @BusinessDays + 7

    AND DATEDIFF(dd,0,t.N+@SomeDate)%7 NOT IN (5,6)

    )

    SELECT SomeDate

    FROM cteAddBusinessDay

    WHERE Businessday = @BusinessDays

    AND STILL, Jack Corbett is correct... if you need to do this a lot, you really need to build a Calendar table with the correct offset.

    --Jeff Moden


    RBAR is pronounced "ree-bar" and is a "Modenism" for Row-By-Agonizing-Row.
    First step towards the paradigm shift of writing Set Based code:
    ________Stop thinking about what you want to do to a ROW... think, instead, of what you want to do to a COLUMN.

    Change is inevitable... Change for the better is not.


    Helpful Links:
    How to post code problems
    How to Post Performance Problems
    Create a Tally Function (fnTally)

  • Here's a method of calculating the Nth working day in the future without using a CTE or tally table.

    DECLARE @days int

    SELECT @days = 10

    DECLARE @inputDate datetime

    SELECT @inputDate = '2008-12-23'

    DECLARE @weekDay int

    SELECT @weekDay = DATEDIFF(day, 0, @inputDate) % 7

    /* @weekDay = 0 (Monday), 2 (Tuesday), ... , 5 (Saturday), 6 (Sunday) */

    DECLARE @dayOffset int

    SELECT @dayOffset = CASE @weekDay

    WHEN 6 THEN @days + (@days / 5) * 2 /* Sunday */

    WHEN 5 THEN 1 + @days + (@days / 5) * 2 /* Saturday */

    ELSE @days + ((@days + @weekDay) / 5) * 2 END

    SELECT DATEADD(day, @dayOffset, @inputDate)

    The method only works if [font="Courier New"]@days > 0[/font].

    If [font="Courier New"]@inputDate[/font] is guaranteed to be a working day (neither Saturday nor Sunday), then the expression simplifies to:

    DECLARE @days int

    SELECT @days = 10

    DECLARE @inputDate datetime

    SELECT @inputDate = '2008-12-23'

    SELECT DATEADD(day, @days + ((@days + DATEDIFF(day, 0, @inputDate) % 7 ) / 5) * 2, @inputDate)

  • andrewd.smith (12/23/2008)


    Here's a method of calculating the Nth working day in the future without using a CTE or tally table.

    DECLARE @days int

    SELECT @days = 10

    DECLARE @inputDate datetime

    SELECT @inputDate = '2008-12-23'

    DECLARE @weekDay int

    SELECT @weekDay = DATEDIFF(day, 0, @inputDate) % 7

    /* @weekDay = 0 (Monday), 2 (Tuesday), ... , 5 (Saturday), 6 (Sunday) */

    DECLARE @dayOffset int

    SELECT @dayOffset = CASE @weekDay

    WHEN 6 THEN @days + (@days / 5) * 2 /* Sunday */

    WHEN 5 THEN 1 + @days + (@days / 5) * 2 /* Saturday */

    ELSE @days + ((@days + @weekDay) / 5) * 2 END

    SELECT DATEADD(day, @dayOffset, @inputDate)

    The method only works if [font="Courier New"]@days > 0[/font].

    If [font="Courier New"]@inputDate[/font] is guaranteed to be a working day (neither Saturday nor Sunday), then the expression simplifies to:

    DECLARE @days int

    SELECT @days = 10

    DECLARE @inputDate datetime

    SELECT @inputDate = '2008-12-23'

    SELECT DATEADD(day, @days + ((@days + DATEDIFF(day, 0, @inputDate) % 7 ) / 5) * 2, @inputDate)

    Very cool... I've gotta look around and see if I can find Serqiy's formula for this... I never did test it but it looked a bit like this one.

    --Jeff Moden


    RBAR is pronounced "ree-bar" and is a "Modenism" for Row-By-Agonizing-Row.
    First step towards the paradigm shift of writing Set Based code:
    ________Stop thinking about what you want to do to a ROW... think, instead, of what you want to do to a COLUMN.

    Change is inevitable... Change for the better is not.


    Helpful Links:
    How to post code problems
    How to Post Performance Problems
    Create a Tally Function (fnTally)

  • Jeff Moden (12/23/2008)


    andrewd.smith (12/23/2008)


    Here's a method of calculating the Nth working day in the future without using a CTE or tally table.

    DECLARE @days int

    SELECT @days = 10

    DECLARE @inputDate datetime

    SELECT @inputDate = '2008-12-23'

    DECLARE @weekDay int

    SELECT @weekDay = DATEDIFF(day, 0, @inputDate) % 7

    /* @weekDay = 0 (Monday), 2 (Tuesday), ... , 5 (Saturday), 6 (Sunday) */

    DECLARE @dayOffset int

    SELECT @dayOffset = CASE @weekDay

    WHEN 6 THEN @days + (@days / 5) * 2 /* Sunday */

    WHEN 5 THEN 1 + @days + (@days / 5) * 2 /* Saturday */

    ELSE @days + ((@days + @weekDay) / 5) * 2 END

    SELECT DATEADD(day, @dayOffset, @inputDate)

    The method only works if [font="Courier New"]@days > 0[/font].

    If [font="Courier New"]@inputDate[/font] is guaranteed to be a working day (neither Saturday nor Sunday), then the expression simplifies to:

    DECLARE @days int

    SELECT @days = 10

    DECLARE @inputDate datetime

    SELECT @inputDate = '2008-12-23'

    SELECT DATEADD(day, @days + ((@days + DATEDIFF(day, 0, @inputDate) % 7 ) / 5) * 2, @inputDate)

    Very cool... I've gotta look around and see if I can find Serqiy's formula for this... I never did test it but it looked a bit like this one.

    This is what and love and hate about making the first post, it's almost never the best one.

  • Marc (12/23/2008)


    Thanks Jack and Jeffrey for all your help!

    I believe this is what I need.

    Happy Holidays,

    Marc

    Are you sure? As mentioned before by others, you probably need a calendar table that stores the holidays.

    When you described what you need, you mentioned that users fill out requests for business days. I am pretty sure that in the past few days at least one non-business day fell between Monday and Friday.

    I would reconsider a calender table if I were you.

    You can then do a simple select like this:

    SELECT MIN(calen_dt) FROM dbo.Calender WHERE calen_dt >= DATEADD(d, @intErval, @dteCurrentDate) AND flg_bdate='Y'

    Best Regards,

    Chris Büttner

  • Thanks Chris and everyone else for your insights. Lots of great info and examples for me to test.

    Marc

Viewing 15 posts - 1 through 15 (of 19 total)

You must be logged in to reply to this topic. Login to reply