Calculating due date using business hours and holidaysHow to return only the Date from a SQL Server DateTime datatypeAdd business days to date in SQL without loopsMySQL time/date calculationCalculating Business Hours in SQLSQL Server 2008 - Sum business minutes between two dates taking into account custom holidays and weekendstime/date calculationsql working days holidaysdifference between two dates without weekends and holidays Sql query ORACLECalculate Expected End Time In SQL Servercalculate hours excluding weekends, holidays and non-working hours with SSMS

How do I ensure my employees don't abuse my flexible work hours policy?

How to securely dispose of a smartphone?

Undetectable mail tracker

A* pathfinding algorithm too slow

Cooking a nice pan seared steak for picky eaters

I just started should I accept a farewell lunch for a coworker I don't know?

What was the point of separating stdout and stderr?

Converting Geographic Coordinates into Lambert2008 coordinates

Why doesn't SpaceX land boosters in Africa?

Why was p[:] designed to work differently in these two situations?

Journal standards vs. personal standards

List Manipulation : a,b,c,d,e,f,g,h into a,b,c,d,e,f,g,h

80's-90's TV show or movie about life clocks

What happens if a caster is surprised while casting a spell with a long casting time?

What do you call a notepad used to keep a record?

Sharing referee/AE report online to point out a grievous error in refereeing

Is it okay to fade a human face just to create some space to place important content over it?

How useful would a hydroelectric plant be in the post-apocalypse world?

What election rules and voting rights are guaranteed by the US Constitution?

Copy group of files (Filename*) to backup (Filename*.bak)

The Lucas argument vs the theorem-provers--who wins and why?

If two black hole event horizons overlap (touch) can they ever separate again?

Closest Proximity of Oceans to Freshwater Springs

Why was Pan Am Flight 103 flying over Lockerbie?



Calculating due date using business hours and holidays


How to return only the Date from a SQL Server DateTime datatypeAdd business days to date in SQL without loopsMySQL time/date calculationCalculating Business Hours in SQLSQL Server 2008 - Sum business minutes between two dates taking into account custom holidays and weekendstime/date calculationsql working days holidaysdifference between two dates without weekends and holidays Sql query ORACLECalculate Expected End Time In SQL Servercalculate hours excluding weekends, holidays and non-working hours with SSMS






.everyoneloves__top-leaderboard:empty,.everyoneloves__mid-leaderboard:empty,.everyoneloves__bot-mid-leaderboard:empty margin-bottom:0;








7















I need to calculate due date / end date for SLAs. As input values I have the start date and a timespan (in minutes). This calculation needs to take into account business hours, weekends, and holidays.



I've seen a lot of examples where the input is start date and end date, but have been struggling finding anything similar to the above input values.



Is there an elegant solution to this problem? Is there a way to calculate due date without using a loop? I can't think of a way to do the calculation without doing something similar to the following terrible algorithm:



  1. Create a return variable "due date" and set it to input variable
    "start date"

  2. Create a control variable "used minutes" and set it to 0

  3. Create a loop with the condition "used minutes" <= "input timespan"

  4. Inside the loop, add a second to the "due date" return variable

  5. Inside the loop, check if the second is within hours of operation
    (checking business hours, weekends, and holidays). If so, increment
    control variable "used minutes" by 1.

  6. Upon exiting the loop, return variable "due date"









share|improve this question

















  • 2





    You need to give an example of some inputs and expected results if you hope to get an answer that works. You also need to answer some questions, such as: are business hours the same for each business day?, and what do you consider holidays? But I think you can do this without either a loop or a table of dates and business hours.

    – Solomon Rutzky
    Jan 2 '15 at 2:58











  • can you mention a sample, and yes you can do it without a loop, just want a sample in other to write the code as you want

    – Monah
    Jan 6 '15 at 17:02











  • This looks like possible duplicate of: stackoverflow.com/questions/1044688/…

    – panpernicek
    Jan 7 '15 at 14:52

















7















I need to calculate due date / end date for SLAs. As input values I have the start date and a timespan (in minutes). This calculation needs to take into account business hours, weekends, and holidays.



I've seen a lot of examples where the input is start date and end date, but have been struggling finding anything similar to the above input values.



Is there an elegant solution to this problem? Is there a way to calculate due date without using a loop? I can't think of a way to do the calculation without doing something similar to the following terrible algorithm:



  1. Create a return variable "due date" and set it to input variable
    "start date"

  2. Create a control variable "used minutes" and set it to 0

  3. Create a loop with the condition "used minutes" <= "input timespan"

  4. Inside the loop, add a second to the "due date" return variable

  5. Inside the loop, check if the second is within hours of operation
    (checking business hours, weekends, and holidays). If so, increment
    control variable "used minutes" by 1.

  6. Upon exiting the loop, return variable "due date"









share|improve this question

















  • 2





    You need to give an example of some inputs and expected results if you hope to get an answer that works. You also need to answer some questions, such as: are business hours the same for each business day?, and what do you consider holidays? But I think you can do this without either a loop or a table of dates and business hours.

    – Solomon Rutzky
    Jan 2 '15 at 2:58











  • can you mention a sample, and yes you can do it without a loop, just want a sample in other to write the code as you want

    – Monah
    Jan 6 '15 at 17:02











  • This looks like possible duplicate of: stackoverflow.com/questions/1044688/…

    – panpernicek
    Jan 7 '15 at 14:52













7












7








7


3






I need to calculate due date / end date for SLAs. As input values I have the start date and a timespan (in minutes). This calculation needs to take into account business hours, weekends, and holidays.



I've seen a lot of examples where the input is start date and end date, but have been struggling finding anything similar to the above input values.



Is there an elegant solution to this problem? Is there a way to calculate due date without using a loop? I can't think of a way to do the calculation without doing something similar to the following terrible algorithm:



  1. Create a return variable "due date" and set it to input variable
    "start date"

  2. Create a control variable "used minutes" and set it to 0

  3. Create a loop with the condition "used minutes" <= "input timespan"

  4. Inside the loop, add a second to the "due date" return variable

  5. Inside the loop, check if the second is within hours of operation
    (checking business hours, weekends, and holidays). If so, increment
    control variable "used minutes" by 1.

  6. Upon exiting the loop, return variable "due date"









share|improve this question














I need to calculate due date / end date for SLAs. As input values I have the start date and a timespan (in minutes). This calculation needs to take into account business hours, weekends, and holidays.



I've seen a lot of examples where the input is start date and end date, but have been struggling finding anything similar to the above input values.



Is there an elegant solution to this problem? Is there a way to calculate due date without using a loop? I can't think of a way to do the calculation without doing something similar to the following terrible algorithm:



  1. Create a return variable "due date" and set it to input variable
    "start date"

  2. Create a control variable "used minutes" and set it to 0

  3. Create a loop with the condition "used minutes" <= "input timespan"

  4. Inside the loop, add a second to the "due date" return variable

  5. Inside the loop, check if the second is within hours of operation
    (checking business hours, weekends, and holidays). If so, increment
    control variable "used minutes" by 1.

  6. Upon exiting the loop, return variable "due date"






sql sql-server-2005






share|improve this question













share|improve this question











share|improve this question




share|improve this question










asked Dec 29 '14 at 2:48









user1886415user1886415

1753 silver badges15 bronze badges




1753 silver badges15 bronze badges







  • 2





    You need to give an example of some inputs and expected results if you hope to get an answer that works. You also need to answer some questions, such as: are business hours the same for each business day?, and what do you consider holidays? But I think you can do this without either a loop or a table of dates and business hours.

    – Solomon Rutzky
    Jan 2 '15 at 2:58











  • can you mention a sample, and yes you can do it without a loop, just want a sample in other to write the code as you want

    – Monah
    Jan 6 '15 at 17:02











  • This looks like possible duplicate of: stackoverflow.com/questions/1044688/…

    – panpernicek
    Jan 7 '15 at 14:52












  • 2





    You need to give an example of some inputs and expected results if you hope to get an answer that works. You also need to answer some questions, such as: are business hours the same for each business day?, and what do you consider holidays? But I think you can do this without either a loop or a table of dates and business hours.

    – Solomon Rutzky
    Jan 2 '15 at 2:58











  • can you mention a sample, and yes you can do it without a loop, just want a sample in other to write the code as you want

    – Monah
    Jan 6 '15 at 17:02











  • This looks like possible duplicate of: stackoverflow.com/questions/1044688/…

    – panpernicek
    Jan 7 '15 at 14:52







2




2





You need to give an example of some inputs and expected results if you hope to get an answer that works. You also need to answer some questions, such as: are business hours the same for each business day?, and what do you consider holidays? But I think you can do this without either a loop or a table of dates and business hours.

– Solomon Rutzky
Jan 2 '15 at 2:58





You need to give an example of some inputs and expected results if you hope to get an answer that works. You also need to answer some questions, such as: are business hours the same for each business day?, and what do you consider holidays? But I think you can do this without either a loop or a table of dates and business hours.

– Solomon Rutzky
Jan 2 '15 at 2:58













can you mention a sample, and yes you can do it without a loop, just want a sample in other to write the code as you want

– Monah
Jan 6 '15 at 17:02





can you mention a sample, and yes you can do it without a loop, just want a sample in other to write the code as you want

– Monah
Jan 6 '15 at 17:02













This looks like possible duplicate of: stackoverflow.com/questions/1044688/…

– panpernicek
Jan 7 '15 at 14:52





This looks like possible duplicate of: stackoverflow.com/questions/1044688/…

– panpernicek
Jan 7 '15 at 14:52












6 Answers
6






active

oldest

votes


















4





+50









You need a table with valid business hours, with the weekends and holidays excluded (or marked as weekend/holiday so you can skip them.) Each row represents one day and the number of working hours for that day. Then you query the business hours table from your start date to the first (min) date where the sum(hours*60) is greater than your minutes parameter, excluding marked weekend/holiday rows. That gives you your end date.



Here's the day table:



CREATE TABLE [dbo].[tblDay](
[dt] [datetime] NOT NULL,
[dayOfWk] [int] NULL,
[dayOfWkInMo] [int] NULL,
[isWeekend] [bit] NOT NULL,
[holidayID] [int] NULL,
[workingDayCount] [int] NULL,
CONSTRAINT [PK_tblDay] PRIMARY KEY CLUSTERED
(
[dt] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]


here's how I populate the table with days:



CREATE PROCEDURE [dbo].[usp_tblDay]
AS
BEGIN
SET NOCOUNT ON;
DECLARE
@Dt datetime ,
@wkInMo int,
@firstDwOfMo int,
@holID int,
@workDayCount int,
@weekday int,
@month int,
@day int,
@isWkEnd bit

set @workDayCount = 0
SET @Dt = CONVERT( datetime, '2008-01-01' )
while @dt < '2020-01-01'
begin
delete from tblDay where dt = @dt

set @weekday = datepart( weekday, @Dt )
set @month = datepart(month,@dt)
set @day = datepart(day,@dt)

if @day = 1 -- 1st of mo
begin
set @wkInMo = 1
set @firstDwOfMo = @weekday
end

if ((@weekday = 7) or (@weekday = 1))
set @isWkEnd = 1
else
set @isWkEnd = 0

if @isWkEnd = 0 and (@month = 1 and @day = 1)
set @holID=1 -- new years on workday
else if @weekday= 6 and (@month = 12 and @day = 31)
set @holID=1 -- holiday on sat, change to fri
else if @weekday= 2 and (@month = 1 and @day = 2)
set @holID=1 -- holiday on sun, change to mon

else if @wkInMo = 3 and @weekday= 2 and @month = 1
set @holID = 2 -- mlk

else if @wkInMo = 3 and @weekday= 2 and @month = 2
set @holID = 3 -- President’s

else if @wkInMo = 4 and @weekday= 2 and @month = 5 and datepart(month,@dt+7) = 6
set @holID = 4 -- memorial on 4th mon, no 5th
else if @wkInMo = 5 and @weekday= 2 and @month = 5
set @holID = 4 -- memorial on 5th mon

else if @isWkEnd = 0 and (@month = 7 and @day = 4)
set @holID=5 -- July 4 on workday
else if @weekday= 6 and (@month = 7 and @day = 3)
set @holID=5 -- holiday on sat, change to fri
else if @weekday= 2 and (@month = 7 and @day = 5)
set @holID=5 -- holiday on sun, change to mon

else if @wkInMo = 1 and @weekday= 2 and @month = 9
set @holID = 6 -- Labor

else if @isWkEnd = 0 and (@month = 11 and @day = 11)
set @holID=7 -- Vets day on workday
else if @weekday= 6 and (@month = 11 and @day = 10)
set @holID=7 -- holiday on sat, change to fri
else if @weekday= 2 and (@month = 11 and @day = 12)
set @holID=7 -- holiday on sun, change to mon

else if @wkInMo = 4 and @weekday= 5 and @month = 11
set @holID = 8 -- thx

else if @holID = 8
set @holID = 9 -- dy after thx

else if @isWkEnd = 0 and (@month = 12 and @day = 25)
set @holID=10 -- xmas day on workday
else if @weekday= 6 and (@month = 12 and @day = 24)
set @holID=10 -- holiday on sat, change to fri
else if @weekday= 2 and (@month = 12 and @day = 26)
set @holID=10 -- holiday on sun, change to mon
else
set @holID = null

insert into tblDay select @dt,@weekday,@wkInMo,@isWkEnd,@holID,@workDayCount

if @isWkEnd=0 and @holID is null
set @workDayCount = @workDayCount + 1

set @dt = @dt + 1
if datepart( weekday, @Dt ) = @firstDwOfMo
set @wkInMo = @wkInMo + 1
end
END


I also have a holiday table, but everyone's holidays are different:



holidayID holiday rule description
1 New Year's Day Jan. 1
2 Martin Luther King Day third Mon. in Jan.
3 Presidents' Day third Mon. in Feb.
4 Memorial Day last Mon. in May
5 Independence Day 4-Jul
6 Labor Day first Mon. in Sept
7 Veterans' Day Nov. 11
8 Thanksgiving fourth Thurs. in Nov.
9 Fri after Thanksgiving Friday after Thanksgiving
10 Christmas Day Dec. 25


HTH






share|improve this answer
































    2














    This is the best I could do, still uses a loop but uses date functions instead of incrementing a minutes variable. Hope you like it.





    --set up our source data
    declare @business_hours table
    (
    work_day varchar(10),
    open_time varchar(8),
    close_time varchar(8)
    )
    insert into @business_hours values ('Monday', '08:30:00', '17:00:00')
    insert into @business_hours values ('Tuesday', '08:30:00', '17:00:00')
    insert into @business_hours values ('Wednesday', '08:30:00', '17:00:00')
    insert into @business_hours values ('Thursday', '08:30:00', '17:00:00')
    insert into @business_hours values ('Friday', '08:30:00', '18:00:00')
    insert into @business_hours values ('Saturday', '09:00:00', '14:00:00')

    declare @holidays table
    (
    holiday varchar(10)
    )
    insert into @holidays values ('2015-01-01')
    insert into @holidays values ('2015-01-02')



    --Im going to assume the SLA of 2 standard business days (0900-1700) = 8*60*2 = 960
    declare @start_date datetime = '2014-12-31 16:12:47'
    declare @time_span int = 960-- time till due in minutes



    declare @true bit = 'true'
    declare @false bit = 'false'



    declare @due_date datetime --our output



    --other variables
    declare @date_string varchar(10)
    declare @today_closing datetime
    declare @is_workday bit = @true
    declare @is_holiday bit = @false



    --Given our timespan is in minutes, lets also assume we dont care about seconds in start or due dates
    set @start_date = DATEADD(ss,datepart(ss,@start_date)*-1,@start_date)



    while (@time_span > 0)
    begin



    set @due_date = DATEADD(MINUTE,@time_span,@start_date)
    set @date_string = FORMAT(DATEADD(dd, 0, DATEDIFF(dd, 0, @start_date)),'yyyy-MM-dd')
    set @today_closing = (select convert(datetime,@date_string + ' ' + close_time) from @business_hours where work_day = DATENAME(weekday,@start_date))

    if exists((select work_day from @business_hours where work_day = DATENAME(weekday,@start_date)))
    set @is_workday = @true
    else
    set @is_workday = @false

    if exists(select holiday from @holidays where holiday = @date_string)
    set @is_holiday = @true
    else
    set @is_holiday = @false

    if @is_workday = @true and @is_holiday = @false
    begin
    if @due_date > @today_closing
    set @time_span = @time_span - datediff(MINUTE, @start_date, @today_closing)
    else
    set @time_span = @time_span - datediff(minute, @start_date, @due_date)
    end

    set @date_string = FORMAT(DATEADD(dd, 1, DATEDIFF(dd, 0, @start_date)),'yyyy-MM-dd')
    set @start_date = CONVERT(datetime, @date_string + ' ' + isnull((select open_time from @business_hours where work_day = DATENAME(weekday,convert(datetime,@date_string))),''))


    end



    select @due_date








    share|improve this answer






























      1














      Here is an option using a WorkSchedule table, which will contain the business hours that are available to count towards the SLA. To account for weekends and holidays, just do not insert records for these days into the WorkSchedule table.



      This solution also uses a "Tally" table aka numbers table in the due date calc. I also included debug output to help you see what is going on, so just comment out or uncomment any debug sections to see less/more info.



      I used SQL temp tables in this example so that you can run it without messing up your current database schema, but you should replace with physical tables if you use this solution.



      Test Data setups:



      CREATE TABLE #WorkSchedule(WorkStart datetime not null primary key, WorkEnd datetime not null);
      GO
      CREATE TABLE #Tally (N int not null primary key);
      GO

      --POPULATE TEST DATA
      --populate Tally table
      insert into #Tally (N)
      select top 10000 N = row_number() over(order by o.object_id)
      from sys.objects o cross apply sys.objects o2
      ;
      go

      --POPULATE WITH DUMMY TEST DATA
      INSERT INTO #WorkSchedule(WorkStart, WorkEnd)
      SELECT
      workStart = dateadd(hour, 8, t.workDate)
      , workEnd = dateadd(hour, 17, t.workDate)
      FROM (
      SELECT top 10000 workDate = dateadd(day, row_number() over(order by o.object_id), '2000-01-01')
      FROM sys.objects o cross apply sys.objects o2
      ) t
      --Exclude weekends from work schedule
      WHERE datename(weekday, t.workDate) not in ('Saturday','Sunday')
      ;

      GO


      Code to calculate Due Date:



      SET NOCOUNT ON;
      DECLARE @startDate datetime;
      DECLARE @SLA_timespan_mins int;

      DECLARE @workStartDayOne datetime;
      DECLARE @SLA_Adjusted int;
      DECLARE @dueDate datetime;

      --SET PARAM VALUES HERE FOR TESTING TO ANY DATE/SLA TIMESPAN YOU WANT:
      SET @startDate = '2014-01-04 05:00'; --Saturday
      SET @SLA_timespan_mins = 10 * 60 ; --10 hrs.

      --get the info day 1, since your start date might be after the work start time.
      select top 1 @workStartDayOne = s.WorkStart
      --increase the SLA timespan mins to account for difference between work start and start time
      , @SLA_Adjusted = case when @startDate > s.WorkStart then datediff(minute, s.WorkStart, @startDate) else 0 end + @SLA_timespan_mins
      from #WorkSchedule s
      where s.WorkEnd > @startDate
      and s.WorkStart <> s.WorkEnd
      order by s.WorkStart asc
      ;

      --DEBUG info:
      select 'Debug Info' as DebugInfo, @startDate AS StartDate, @workStartDayOne as workStartDayOne, @SLA_timespan_mins as SLA_timespan_mins, @SLA_Adjusted as SLA_Adjusted;

      --now sum all the non work hours during that period and determine the additional mins that need added.
      ;with cteWorkMins as
      (
      SELECT TOP (@SLA_Adjusted)
      s.WorkStart, s.WorkEnd
      , WorkMinute = dateadd(minute, t.N, cast(s.WorkStart as datetime))
      , t.N as MinuteOfWorkDay
      , RowNum = row_number() over(order by s.WorkStart, t.N)
      FROM #WorkSchedule s
      INNER JOIN #Tally t
      ON t.N between 1 and datediff(minute, s.WorkStart, s.WorkEnd)
      WHERE s.WorkStart >= @workStartDayOne
      ORDER BY s.WorkStart, t.N
      )
      /**/
      SELECT @dueDate = m.WorkMinute
      FROM cteWorkMins m
      WHERE m.RowNum = @SLA_Adjusted
      --*/
      /**
      --DEBUG: this query will show every minute that is accounted for during the Due Date calculation.
      SELECT m.*
      FROM cteWorkMins m
      --WHERE m.RowNum = @SLA_Adjusted
      ORDER BY m.WorkMinute
      --*/
      ;

      select @dueDate as DueDate;
      GO


      Test Cleanup:



      IF object_id('TEMPDB..#WorkSchedule') IS NOT NULL
      DROP TABLE #WorkSchedule;
      GO
      IF object_id('TEMPDB..#Tally') IS NOT NULL
      DROP TABLE #Tally;


      GO






      share|improve this answer






























        1














        as I understood from your question, what you need is as follow



        1. You have given start date and number of minutes added to it, then you need to get the due date

        2. To get the due date, you need to exclude the holidays and the due date should be during business day

        here is what you can do



        declare @start datetime,
        @min int,
        @days int

        set @start= '28 Dec 2014'
        set @min = 2200

        -- get the number of days
        set @days=datediff(day,@start,dateadd(minute,@min,@start))

        -- get the due date
        select max(Date)
        from
        (select row_number() over( order by t.Id)-1 as Id,t.Date
        from DateMetadata t
        inner join BusinessDays b on Day(t.Date) = b.Day
        where t.Date > = @start and not exists(select 1 from Holidays h
        where h.Day=Day(t.Date)
        and h.Month=Month(t.Date))) as p
        where p.Id < @days


        Note :that DateMetadata table you will setup it in your database once



        the setup for the above code :



        create table Holidays(Id int identity(1,1),
        Name nvarchar(50),
        Day int,
        Month int)
        create table BusinessDays(Id int identity(1,1),
        Name nvarchar(20),
        Day int)

        -- i am putting some days that are known,
        -- it depends on you to define which holidays you want
        insert into Holidays (Name,Day,Month) values('Christmas',25,12)
        insert into Holidays(Name,Day,Month) values('New Year',31,12)
        insert into Holidays(Name,Day,Month) values('Valentine',14,2)
        insert into Holidays(Name,Day,Month) values('Mothers day',21,3)
        insert into Holidays(Name,Day,Month) values('April fools day',1,4)

        -- i am assuming that the business days are from monday till friday and
        -- saturday and sunday are off days
        insert into BusinessDays(Name,Day) values ('Monday',1)
        insert into BusinessDays(Name,Day) values('Tuesday',2)
        insert into BusinessDays(Name,Day) values('Wednesday',3)
        insert into BusinessDays(Name,Day) values('Thursday',4)
        insert into BusinessDays(Name,Day) values('Friday',5)


        this table is needed and you will setup it once



        -- set up a table that contains all dates from now till 2050 for example
        -- and you can change the value of 2050 depending on your needs
        -- this table you will setup it once
        create table DateMetadata(Id int identity(1,1),
        Date datetime)

        declare @date datetime
        set @date='01 Jan 2014'
        while @date < '31 Dec 2050'
        begin
        insert into DateMetadata(Date) values(@date)
        set @date = @date + 1
        end


        here a working DEMO



        if you need any explanation, i am ready



        hope it will help you






        share|improve this answer


















        • 1





          That is not how holidays work. Some of them, yes, but not for anything that changes per year (i.e. Thanksgiving = 4th Thursday in November, and July 4th could be observed on either the 3rd or 5th if the 4th is on a Saturday or Sunday, respectively).

          – Solomon Rutzky
          Jan 6 '15 at 18:31






        • 1





          @srutzky at the end it depends on the country he is from and the table i defined is somehow flexible to define any holiday he wants independent from the year, i looked to the calendar and i picked some

          – Monah
          Jan 6 '15 at 18:38



















        1














        Sql to Calculate due date excluding holidays and considering business hour as below :- < note : - It's working correctly Business hour (8-5) Maintain holiday table



        CREATE TABLE [dbo].[holiday](
        [id] [int] IDENTITY(1,1) NOT NULL,
        [region] [nvarchar](10) NULL,
        [Hdate] [date] NULL,
        )


        >



        declare @start datetime= getdate()
        declare @slamins int =960 --- SLA time in mins
        declare @Country varchar(2)='NA'
        declare @start_hour int = 8 -- business start hour
        declare @end_hour int = 17 -- business end hour
        declare @true bit = 'true'
        declare @false bit = 'false'
        declare @is_workday bit = @true
        declare @is_holiday bit = @false
        declare @due_date datetime
        declare @today_closing datetime
        declare @temp int = 0
        declare @holidays table (HDate DateTime) -- Table variable to hold holidayes

        ---- Get country holidays from table based on the country code (Feel free to remove this or modify as per your DB schema)
        Insert Into @Holidays (HDate) Select date from HOLIDAY Where region=@Country and Hdate>=DateAdd(dd, DateDiff(dd,0,@start), 0)

        --check for weekends
        set @start = case(datepart(dw,@start)+@@datefirst-1)%7
        when 0 then Convert(dateTime,CONVERT(varchar,@start+1,101)+' 08:00:00')
        when 6 then Convert(dateTime,CONVERT(varchar,@start+2,101)+' 08:00:00')
        else @start end

        -- check if start time is before business hour
        if datepart(hh, @start) < @start_hour set @start = Convert(dateTime,CONVERT(varchar,@start,101)+' 08:00:00')

        -- check if start time is after business hour
        if datepart(hh, @start) >= @end_hour set @start = Convert(dateTime,CONVERT(varchar,@start+1,101)+' 08:00:00')

        -- loop start
        while (@slamins > 0)
        begin
        -- prepared closing date time based on start date
        set @today_closing = Convert(dateTime,CONVERT(varchar,@start,101)+' 17:00:00')
        set @due_date = @start
        -- calculate number of Minute between start date and closing date
        set @temp = DATEDIFF(N, @start , @today_closing);

        --check for weekends
        if (DATEPART(dw, @start)!=1 AND DATEPART(dw, @start)!=7)
        set @is_workday = @true
        else
        set @is_workday = @false
        --check for holidays
        if (Select Count(*) From @Holidays Where HDATE=DateAdd(dd, DateDiff(dd,0,@start), 0)) = 0
        set @is_holiday =@false
        else
        set @is_holiday = @true
        if @is_workday = @true and @is_holiday = @false
        begin

        if(@temp < @slamins)
        begin
        set @slamins = @slamins - @temp
        end
        else
        begin
        set @due_date = DATEADD(MINUTE,@slamins,@start)
        set @slamins = 0
        print @due_date
        end
        end
        set @start = Convert(dateTime,CONVERT(varchar,@start+1,101)+' 08:00:00')
        end

        select @due_date





        share|improve this answer






























          0














          I created a function to calculate due date from the table, once it's populated as per Beth's and others' approaches (various similar methods for doing this, as you can see -- it only took me about an hour to think about all the UK holidays and populate the table including Easter dates up to 2029 without using these exact guides).



          Note that my table contains SLA in business hours (8 hours in a normal day, 5 days in a normal week), your business hours may vary but you can amend this easily, just make sure your business hours are set the same for both the SLA table and the function below.



          Code below is T-SQL (written in SSMS v17.8.1)



          CREATE FUNCTION [JIRA].[Fn_JIRA_Due_Date] (
          @CreatedDate DATETIME, @SLA_Business_Hours INTEGER
          ) RETURNS DATETIME
          AS
          -- SELECT [JIRA].[Fn_JIRA_Due_Date]('2019-12-28 08:00:00', 24)

          /*

          baldmosher™
          2019-03-25

          * Function returns the DueDate for a JIRA ticket, based on the CreatedDate and the SLA (based on the Issue Type, or the Epic for Tasks) and business hours per date (set in [JIRA].[Ref_JIRA_Business_Hours])
          * Called by IUP to store this at the time the ticket is loaded
          * Can only consider SLA in Business Hours:
          * <24hrs calendar = <8hrs business
          * =24hrs calendar = 8hrs business
          * >24hrs calendar = 8hrs business * business days

          */


          BEGIN

          IF @CreatedDate IS NULL OR @SLA_Business_Hours IS NULL RETURN NULL;

          DECLARE @SLA_Hours_Remaining SMALLINT = @SLA_Business_Hours;

          --SET DATEFIRST 1;
          DECLARE @DueDate DATETIME;
          DECLARE @BusHrsStart DECIMAL(18,10) = 8 ; -- start of Business Hours (8am)
          DECLARE @BusHrsClose DECIMAL(18,10) = 16 ; -- close of Business Hours (4pm)
          --DECLARE @WkndStart DECIMAL(18,10) = 6 ; -- start of weekend (Sat)
          DECLARE @Hours_Today SMALLINT ; -- # hours left in day to process ticket

          -- PRINT 'Created ' + CAST(CAST(@CreatedDate AS DATE) AS VARCHAR(10)) + ' ' + CAST(CAST(@CreatedDate AS TIME) AS VARCHAR(8))

          --!!!! extend to the next whole hour just to simplify reporting -- need to work on fixing this eventually
          SET @CreatedDate = DATEADD(MINUTE,60-DATEPART(MINUTE,@CreatedDate),@CreatedDate)
          -- PRINT 'Rounded ' + CAST(CAST(@CreatedDate AS DATE) AS VARCHAR(10)) + ' ' + CAST(CAST(@CreatedDate AS TIME) AS VARCHAR(8))


          --check if created outside business hours and adjust CreatedDate to start the clock first thing at the next business hours start of day (days are checked next)
          IF DATEPART(HOUR,@CreatedDate) < @BusHrsStart
          --created before normal hours, adjust @CreatedDate later to @BusHrsStart same day
          BEGIN
          SET @CreatedDate = DATEADD(HOUR,@BusHrsStart,CAST(CAST(@CreatedDate AS DATE) AS DATETIME))
          END

          IF DATEPART(HOUR,@CreatedDate) >= @BusHrsClose
          --created after normal hours, adjust @CreatedDate to @BusHrsStart next day
          BEGIN
          SET @CreatedDate = DATEADD(HOUR,@BusHrsStart,CAST(CAST(@CreatedDate+1 AS DATE) AS DATETIME))
          --adjust CreatedDate to start the clock the next day with >0 business hours (i.e. extend if it falls on a weekend or holiday)
          SET @CreatedDate = CAST(@CreatedDate AS DATE)
          StartNextWorkingDay:
          IF (SELECT TOP(1) [Business_Hours] FROM [JIRA].[Ref_JIRA_Business_Hours] b WHERE b.[Date] = @CreatedDate ORDER BY [Date]) = 0
          BEGIN
          SET @CreatedDate = DATEADD(DAY,1,@CreatedDate)
          GOTO StartNextWorkingDay
          END
          --DATEADD(DAY, DATEDIFF(DAY,0,@CreatedDate+7)/7*7,0); -- midnight, Monday next week
          SET @CreatedDate = DATEADD(HOUR,@BusHrsStart,@CreatedDate); -- BusHrsStart
          END
          -- PRINT 'Started ' + CAST(CAST(@CreatedDate AS DATE) AS VARCHAR(10)) + ' ' + CAST(CAST(@CreatedDate AS TIME) AS VARCHAR(8))

          --third, check the business hours for each date from CreatedDate onwards to determine the relevant DueDate
          SET @DueDate = @CreatedDate
          -- PRINT 'SLA Hrs ' + CAST(@SLA_Hours_Remaining AS VARCHAR(2))
          SET @Hours_Today = @BusHrsStart + (SELECT TOP(1) [Business_Hours] FROM [JIRA].[Ref_JIRA_Business_Hours] b WHERE b.[Date] = CAST(@DueDate AS DATE) ORDER BY [Date]) - DATEPART(HOUR, @CreatedDate)
          -- PRINT 'Hrs Today ' + CAST(@Hours_Today AS VARCHAR(2))
          DueNextWorkingDay:
          IF @SLA_Hours_Remaining > @Hours_Today
          BEGIN
          -- PRINT 'Due another day'
          SET @SLA_Hours_Remaining = @SLA_Hours_Remaining - @Hours_Today --adjust remaining time after today's hours
          SET @Hours_Today = (SELECT TOP(1) [Business_Hours] FROM [JIRA].[Ref_JIRA_Business_Hours] b WHERE b.[Date] = CAST(@DueDate AS DATE) ORDER BY [Date])
          -- PRINT 'SLA Hrs ' + CAST(@SLA_Hours_Remaining AS VARCHAR(2))
          -- PRINT 'Hrs Today ' + CAST(@Hours_Today AS VARCHAR(2))
          SET @DueDate = DATEADD(DAY,1,DATEADD(HOUR,@BusHrsStart,CAST(CAST(@DueDate AS DATE) AS DATETIME))) --adjust DueDate to first thing next day
          END
          IF @SLA_Hours_Remaining <= @Hours_Today
          BEGIN
          -- PRINT 'Due today'
          SET @DueDate = DATEADD(HOUR,@SLA_Hours_Remaining,@DueDate)
          END
          ELSE
          BEGIN
          GOTO DueNextWorkingDay
          END

          -- PRINT 'DueDate ' + CAST(CAST(@DueDate AS DATE) AS VARCHAR(10)) + ' ' + CAST(CAST(@DueDate AS TIME) AS VARCHAR(8))

          RETURN @DueDate

          END

          GO


          Table for SLAs:



          CREATE TABLE [JIRA].[Ref_JIRA_SLAs](
          [SLA_SK] [SMALLINT] IDENTITY(1,1) NOT NULL,
          [Project] [VARCHAR](20) NULL,
          [Issue_Type] [VARCHAR](50) NULL,
          [Epic_Name] [VARCHAR](50) NULL,
          [SLA_Business_Hours] [SMALLINT] NULL,
          [Comments] [VARCHAR](8000) NULL
          ) ON [PRIMARY] WITH (DATA_COMPRESSION = PAGE)
          GO





          share|improve this answer



























            Your Answer






            StackExchange.ifUsing("editor", function ()
            StackExchange.using("externalEditor", function ()
            StackExchange.using("snippets", function ()
            StackExchange.snippets.init();
            );
            );
            , "code-snippets");

            StackExchange.ready(function()
            var channelOptions =
            tags: "".split(" "),
            id: "1"
            ;
            initTagRenderer("".split(" "), "".split(" "), channelOptions);

            StackExchange.using("externalEditor", function()
            // Have to fire editor after snippets, if snippets enabled
            if (StackExchange.settings.snippets.snippetsEnabled)
            StackExchange.using("snippets", function()
            createEditor();
            );

            else
            createEditor();

            );

            function createEditor()
            StackExchange.prepareEditor(
            heartbeatType: 'answer',
            autoActivateHeartbeat: false,
            convertImagesToLinks: true,
            noModals: true,
            showLowRepImageUploadWarning: true,
            reputationToPostImages: 10,
            bindNavPrevention: true,
            postfix: "",
            imageUploader:
            brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
            contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
            allowUrls: true
            ,
            onDemand: true,
            discardSelector: ".discard-answer"
            ,immediatelyShowMarkdownHelp:true
            );



            );













            draft saved

            draft discarded


















            StackExchange.ready(
            function ()
            StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f27682810%2fcalculating-due-date-using-business-hours-and-holidays%23new-answer', 'question_page');

            );

            Post as a guest















            Required, but never shown

























            6 Answers
            6






            active

            oldest

            votes








            6 Answers
            6






            active

            oldest

            votes









            active

            oldest

            votes






            active

            oldest

            votes









            4





            +50









            You need a table with valid business hours, with the weekends and holidays excluded (or marked as weekend/holiday so you can skip them.) Each row represents one day and the number of working hours for that day. Then you query the business hours table from your start date to the first (min) date where the sum(hours*60) is greater than your minutes parameter, excluding marked weekend/holiday rows. That gives you your end date.



            Here's the day table:



            CREATE TABLE [dbo].[tblDay](
            [dt] [datetime] NOT NULL,
            [dayOfWk] [int] NULL,
            [dayOfWkInMo] [int] NULL,
            [isWeekend] [bit] NOT NULL,
            [holidayID] [int] NULL,
            [workingDayCount] [int] NULL,
            CONSTRAINT [PK_tblDay] PRIMARY KEY CLUSTERED
            (
            [dt] ASC
            )WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
            ) ON [PRIMARY]


            here's how I populate the table with days:



            CREATE PROCEDURE [dbo].[usp_tblDay]
            AS
            BEGIN
            SET NOCOUNT ON;
            DECLARE
            @Dt datetime ,
            @wkInMo int,
            @firstDwOfMo int,
            @holID int,
            @workDayCount int,
            @weekday int,
            @month int,
            @day int,
            @isWkEnd bit

            set @workDayCount = 0
            SET @Dt = CONVERT( datetime, '2008-01-01' )
            while @dt < '2020-01-01'
            begin
            delete from tblDay where dt = @dt

            set @weekday = datepart( weekday, @Dt )
            set @month = datepart(month,@dt)
            set @day = datepart(day,@dt)

            if @day = 1 -- 1st of mo
            begin
            set @wkInMo = 1
            set @firstDwOfMo = @weekday
            end

            if ((@weekday = 7) or (@weekday = 1))
            set @isWkEnd = 1
            else
            set @isWkEnd = 0

            if @isWkEnd = 0 and (@month = 1 and @day = 1)
            set @holID=1 -- new years on workday
            else if @weekday= 6 and (@month = 12 and @day = 31)
            set @holID=1 -- holiday on sat, change to fri
            else if @weekday= 2 and (@month = 1 and @day = 2)
            set @holID=1 -- holiday on sun, change to mon

            else if @wkInMo = 3 and @weekday= 2 and @month = 1
            set @holID = 2 -- mlk

            else if @wkInMo = 3 and @weekday= 2 and @month = 2
            set @holID = 3 -- President’s

            else if @wkInMo = 4 and @weekday= 2 and @month = 5 and datepart(month,@dt+7) = 6
            set @holID = 4 -- memorial on 4th mon, no 5th
            else if @wkInMo = 5 and @weekday= 2 and @month = 5
            set @holID = 4 -- memorial on 5th mon

            else if @isWkEnd = 0 and (@month = 7 and @day = 4)
            set @holID=5 -- July 4 on workday
            else if @weekday= 6 and (@month = 7 and @day = 3)
            set @holID=5 -- holiday on sat, change to fri
            else if @weekday= 2 and (@month = 7 and @day = 5)
            set @holID=5 -- holiday on sun, change to mon

            else if @wkInMo = 1 and @weekday= 2 and @month = 9
            set @holID = 6 -- Labor

            else if @isWkEnd = 0 and (@month = 11 and @day = 11)
            set @holID=7 -- Vets day on workday
            else if @weekday= 6 and (@month = 11 and @day = 10)
            set @holID=7 -- holiday on sat, change to fri
            else if @weekday= 2 and (@month = 11 and @day = 12)
            set @holID=7 -- holiday on sun, change to mon

            else if @wkInMo = 4 and @weekday= 5 and @month = 11
            set @holID = 8 -- thx

            else if @holID = 8
            set @holID = 9 -- dy after thx

            else if @isWkEnd = 0 and (@month = 12 and @day = 25)
            set @holID=10 -- xmas day on workday
            else if @weekday= 6 and (@month = 12 and @day = 24)
            set @holID=10 -- holiday on sat, change to fri
            else if @weekday= 2 and (@month = 12 and @day = 26)
            set @holID=10 -- holiday on sun, change to mon
            else
            set @holID = null

            insert into tblDay select @dt,@weekday,@wkInMo,@isWkEnd,@holID,@workDayCount

            if @isWkEnd=0 and @holID is null
            set @workDayCount = @workDayCount + 1

            set @dt = @dt + 1
            if datepart( weekday, @Dt ) = @firstDwOfMo
            set @wkInMo = @wkInMo + 1
            end
            END


            I also have a holiday table, but everyone's holidays are different:



            holidayID holiday rule description
            1 New Year's Day Jan. 1
            2 Martin Luther King Day third Mon. in Jan.
            3 Presidents' Day third Mon. in Feb.
            4 Memorial Day last Mon. in May
            5 Independence Day 4-Jul
            6 Labor Day first Mon. in Sept
            7 Veterans' Day Nov. 11
            8 Thanksgiving fourth Thurs. in Nov.
            9 Fri after Thanksgiving Friday after Thanksgiving
            10 Christmas Day Dec. 25


            HTH






            share|improve this answer





























              4





              +50









              You need a table with valid business hours, with the weekends and holidays excluded (or marked as weekend/holiday so you can skip them.) Each row represents one day and the number of working hours for that day. Then you query the business hours table from your start date to the first (min) date where the sum(hours*60) is greater than your minutes parameter, excluding marked weekend/holiday rows. That gives you your end date.



              Here's the day table:



              CREATE TABLE [dbo].[tblDay](
              [dt] [datetime] NOT NULL,
              [dayOfWk] [int] NULL,
              [dayOfWkInMo] [int] NULL,
              [isWeekend] [bit] NOT NULL,
              [holidayID] [int] NULL,
              [workingDayCount] [int] NULL,
              CONSTRAINT [PK_tblDay] PRIMARY KEY CLUSTERED
              (
              [dt] ASC
              )WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
              ) ON [PRIMARY]


              here's how I populate the table with days:



              CREATE PROCEDURE [dbo].[usp_tblDay]
              AS
              BEGIN
              SET NOCOUNT ON;
              DECLARE
              @Dt datetime ,
              @wkInMo int,
              @firstDwOfMo int,
              @holID int,
              @workDayCount int,
              @weekday int,
              @month int,
              @day int,
              @isWkEnd bit

              set @workDayCount = 0
              SET @Dt = CONVERT( datetime, '2008-01-01' )
              while @dt < '2020-01-01'
              begin
              delete from tblDay where dt = @dt

              set @weekday = datepart( weekday, @Dt )
              set @month = datepart(month,@dt)
              set @day = datepart(day,@dt)

              if @day = 1 -- 1st of mo
              begin
              set @wkInMo = 1
              set @firstDwOfMo = @weekday
              end

              if ((@weekday = 7) or (@weekday = 1))
              set @isWkEnd = 1
              else
              set @isWkEnd = 0

              if @isWkEnd = 0 and (@month = 1 and @day = 1)
              set @holID=1 -- new years on workday
              else if @weekday= 6 and (@month = 12 and @day = 31)
              set @holID=1 -- holiday on sat, change to fri
              else if @weekday= 2 and (@month = 1 and @day = 2)
              set @holID=1 -- holiday on sun, change to mon

              else if @wkInMo = 3 and @weekday= 2 and @month = 1
              set @holID = 2 -- mlk

              else if @wkInMo = 3 and @weekday= 2 and @month = 2
              set @holID = 3 -- President’s

              else if @wkInMo = 4 and @weekday= 2 and @month = 5 and datepart(month,@dt+7) = 6
              set @holID = 4 -- memorial on 4th mon, no 5th
              else if @wkInMo = 5 and @weekday= 2 and @month = 5
              set @holID = 4 -- memorial on 5th mon

              else if @isWkEnd = 0 and (@month = 7 and @day = 4)
              set @holID=5 -- July 4 on workday
              else if @weekday= 6 and (@month = 7 and @day = 3)
              set @holID=5 -- holiday on sat, change to fri
              else if @weekday= 2 and (@month = 7 and @day = 5)
              set @holID=5 -- holiday on sun, change to mon

              else if @wkInMo = 1 and @weekday= 2 and @month = 9
              set @holID = 6 -- Labor

              else if @isWkEnd = 0 and (@month = 11 and @day = 11)
              set @holID=7 -- Vets day on workday
              else if @weekday= 6 and (@month = 11 and @day = 10)
              set @holID=7 -- holiday on sat, change to fri
              else if @weekday= 2 and (@month = 11 and @day = 12)
              set @holID=7 -- holiday on sun, change to mon

              else if @wkInMo = 4 and @weekday= 5 and @month = 11
              set @holID = 8 -- thx

              else if @holID = 8
              set @holID = 9 -- dy after thx

              else if @isWkEnd = 0 and (@month = 12 and @day = 25)
              set @holID=10 -- xmas day on workday
              else if @weekday= 6 and (@month = 12 and @day = 24)
              set @holID=10 -- holiday on sat, change to fri
              else if @weekday= 2 and (@month = 12 and @day = 26)
              set @holID=10 -- holiday on sun, change to mon
              else
              set @holID = null

              insert into tblDay select @dt,@weekday,@wkInMo,@isWkEnd,@holID,@workDayCount

              if @isWkEnd=0 and @holID is null
              set @workDayCount = @workDayCount + 1

              set @dt = @dt + 1
              if datepart( weekday, @Dt ) = @firstDwOfMo
              set @wkInMo = @wkInMo + 1
              end
              END


              I also have a holiday table, but everyone's holidays are different:



              holidayID holiday rule description
              1 New Year's Day Jan. 1
              2 Martin Luther King Day third Mon. in Jan.
              3 Presidents' Day third Mon. in Feb.
              4 Memorial Day last Mon. in May
              5 Independence Day 4-Jul
              6 Labor Day first Mon. in Sept
              7 Veterans' Day Nov. 11
              8 Thanksgiving fourth Thurs. in Nov.
              9 Fri after Thanksgiving Friday after Thanksgiving
              10 Christmas Day Dec. 25


              HTH






              share|improve this answer



























                4





                +50







                4





                +50



                4




                +50





                You need a table with valid business hours, with the weekends and holidays excluded (or marked as weekend/holiday so you can skip them.) Each row represents one day and the number of working hours for that day. Then you query the business hours table from your start date to the first (min) date where the sum(hours*60) is greater than your minutes parameter, excluding marked weekend/holiday rows. That gives you your end date.



                Here's the day table:



                CREATE TABLE [dbo].[tblDay](
                [dt] [datetime] NOT NULL,
                [dayOfWk] [int] NULL,
                [dayOfWkInMo] [int] NULL,
                [isWeekend] [bit] NOT NULL,
                [holidayID] [int] NULL,
                [workingDayCount] [int] NULL,
                CONSTRAINT [PK_tblDay] PRIMARY KEY CLUSTERED
                (
                [dt] ASC
                )WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
                ) ON [PRIMARY]


                here's how I populate the table with days:



                CREATE PROCEDURE [dbo].[usp_tblDay]
                AS
                BEGIN
                SET NOCOUNT ON;
                DECLARE
                @Dt datetime ,
                @wkInMo int,
                @firstDwOfMo int,
                @holID int,
                @workDayCount int,
                @weekday int,
                @month int,
                @day int,
                @isWkEnd bit

                set @workDayCount = 0
                SET @Dt = CONVERT( datetime, '2008-01-01' )
                while @dt < '2020-01-01'
                begin
                delete from tblDay where dt = @dt

                set @weekday = datepart( weekday, @Dt )
                set @month = datepart(month,@dt)
                set @day = datepart(day,@dt)

                if @day = 1 -- 1st of mo
                begin
                set @wkInMo = 1
                set @firstDwOfMo = @weekday
                end

                if ((@weekday = 7) or (@weekday = 1))
                set @isWkEnd = 1
                else
                set @isWkEnd = 0

                if @isWkEnd = 0 and (@month = 1 and @day = 1)
                set @holID=1 -- new years on workday
                else if @weekday= 6 and (@month = 12 and @day = 31)
                set @holID=1 -- holiday on sat, change to fri
                else if @weekday= 2 and (@month = 1 and @day = 2)
                set @holID=1 -- holiday on sun, change to mon

                else if @wkInMo = 3 and @weekday= 2 and @month = 1
                set @holID = 2 -- mlk

                else if @wkInMo = 3 and @weekday= 2 and @month = 2
                set @holID = 3 -- President’s

                else if @wkInMo = 4 and @weekday= 2 and @month = 5 and datepart(month,@dt+7) = 6
                set @holID = 4 -- memorial on 4th mon, no 5th
                else if @wkInMo = 5 and @weekday= 2 and @month = 5
                set @holID = 4 -- memorial on 5th mon

                else if @isWkEnd = 0 and (@month = 7 and @day = 4)
                set @holID=5 -- July 4 on workday
                else if @weekday= 6 and (@month = 7 and @day = 3)
                set @holID=5 -- holiday on sat, change to fri
                else if @weekday= 2 and (@month = 7 and @day = 5)
                set @holID=5 -- holiday on sun, change to mon

                else if @wkInMo = 1 and @weekday= 2 and @month = 9
                set @holID = 6 -- Labor

                else if @isWkEnd = 0 and (@month = 11 and @day = 11)
                set @holID=7 -- Vets day on workday
                else if @weekday= 6 and (@month = 11 and @day = 10)
                set @holID=7 -- holiday on sat, change to fri
                else if @weekday= 2 and (@month = 11 and @day = 12)
                set @holID=7 -- holiday on sun, change to mon

                else if @wkInMo = 4 and @weekday= 5 and @month = 11
                set @holID = 8 -- thx

                else if @holID = 8
                set @holID = 9 -- dy after thx

                else if @isWkEnd = 0 and (@month = 12 and @day = 25)
                set @holID=10 -- xmas day on workday
                else if @weekday= 6 and (@month = 12 and @day = 24)
                set @holID=10 -- holiday on sat, change to fri
                else if @weekday= 2 and (@month = 12 and @day = 26)
                set @holID=10 -- holiday on sun, change to mon
                else
                set @holID = null

                insert into tblDay select @dt,@weekday,@wkInMo,@isWkEnd,@holID,@workDayCount

                if @isWkEnd=0 and @holID is null
                set @workDayCount = @workDayCount + 1

                set @dt = @dt + 1
                if datepart( weekday, @Dt ) = @firstDwOfMo
                set @wkInMo = @wkInMo + 1
                end
                END


                I also have a holiday table, but everyone's holidays are different:



                holidayID holiday rule description
                1 New Year's Day Jan. 1
                2 Martin Luther King Day third Mon. in Jan.
                3 Presidents' Day third Mon. in Feb.
                4 Memorial Day last Mon. in May
                5 Independence Day 4-Jul
                6 Labor Day first Mon. in Sept
                7 Veterans' Day Nov. 11
                8 Thanksgiving fourth Thurs. in Nov.
                9 Fri after Thanksgiving Friday after Thanksgiving
                10 Christmas Day Dec. 25


                HTH






                share|improve this answer















                You need a table with valid business hours, with the weekends and holidays excluded (or marked as weekend/holiday so you can skip them.) Each row represents one day and the number of working hours for that day. Then you query the business hours table from your start date to the first (min) date where the sum(hours*60) is greater than your minutes parameter, excluding marked weekend/holiday rows. That gives you your end date.



                Here's the day table:



                CREATE TABLE [dbo].[tblDay](
                [dt] [datetime] NOT NULL,
                [dayOfWk] [int] NULL,
                [dayOfWkInMo] [int] NULL,
                [isWeekend] [bit] NOT NULL,
                [holidayID] [int] NULL,
                [workingDayCount] [int] NULL,
                CONSTRAINT [PK_tblDay] PRIMARY KEY CLUSTERED
                (
                [dt] ASC
                )WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
                ) ON [PRIMARY]


                here's how I populate the table with days:



                CREATE PROCEDURE [dbo].[usp_tblDay]
                AS
                BEGIN
                SET NOCOUNT ON;
                DECLARE
                @Dt datetime ,
                @wkInMo int,
                @firstDwOfMo int,
                @holID int,
                @workDayCount int,
                @weekday int,
                @month int,
                @day int,
                @isWkEnd bit

                set @workDayCount = 0
                SET @Dt = CONVERT( datetime, '2008-01-01' )
                while @dt < '2020-01-01'
                begin
                delete from tblDay where dt = @dt

                set @weekday = datepart( weekday, @Dt )
                set @month = datepart(month,@dt)
                set @day = datepart(day,@dt)

                if @day = 1 -- 1st of mo
                begin
                set @wkInMo = 1
                set @firstDwOfMo = @weekday
                end

                if ((@weekday = 7) or (@weekday = 1))
                set @isWkEnd = 1
                else
                set @isWkEnd = 0

                if @isWkEnd = 0 and (@month = 1 and @day = 1)
                set @holID=1 -- new years on workday
                else if @weekday= 6 and (@month = 12 and @day = 31)
                set @holID=1 -- holiday on sat, change to fri
                else if @weekday= 2 and (@month = 1 and @day = 2)
                set @holID=1 -- holiday on sun, change to mon

                else if @wkInMo = 3 and @weekday= 2 and @month = 1
                set @holID = 2 -- mlk

                else if @wkInMo = 3 and @weekday= 2 and @month = 2
                set @holID = 3 -- President’s

                else if @wkInMo = 4 and @weekday= 2 and @month = 5 and datepart(month,@dt+7) = 6
                set @holID = 4 -- memorial on 4th mon, no 5th
                else if @wkInMo = 5 and @weekday= 2 and @month = 5
                set @holID = 4 -- memorial on 5th mon

                else if @isWkEnd = 0 and (@month = 7 and @day = 4)
                set @holID=5 -- July 4 on workday
                else if @weekday= 6 and (@month = 7 and @day = 3)
                set @holID=5 -- holiday on sat, change to fri
                else if @weekday= 2 and (@month = 7 and @day = 5)
                set @holID=5 -- holiday on sun, change to mon

                else if @wkInMo = 1 and @weekday= 2 and @month = 9
                set @holID = 6 -- Labor

                else if @isWkEnd = 0 and (@month = 11 and @day = 11)
                set @holID=7 -- Vets day on workday
                else if @weekday= 6 and (@month = 11 and @day = 10)
                set @holID=7 -- holiday on sat, change to fri
                else if @weekday= 2 and (@month = 11 and @day = 12)
                set @holID=7 -- holiday on sun, change to mon

                else if @wkInMo = 4 and @weekday= 5 and @month = 11
                set @holID = 8 -- thx

                else if @holID = 8
                set @holID = 9 -- dy after thx

                else if @isWkEnd = 0 and (@month = 12 and @day = 25)
                set @holID=10 -- xmas day on workday
                else if @weekday= 6 and (@month = 12 and @day = 24)
                set @holID=10 -- holiday on sat, change to fri
                else if @weekday= 2 and (@month = 12 and @day = 26)
                set @holID=10 -- holiday on sun, change to mon
                else
                set @holID = null

                insert into tblDay select @dt,@weekday,@wkInMo,@isWkEnd,@holID,@workDayCount

                if @isWkEnd=0 and @holID is null
                set @workDayCount = @workDayCount + 1

                set @dt = @dt + 1
                if datepart( weekday, @Dt ) = @firstDwOfMo
                set @wkInMo = @wkInMo + 1
                end
                END


                I also have a holiday table, but everyone's holidays are different:



                holidayID holiday rule description
                1 New Year's Day Jan. 1
                2 Martin Luther King Day third Mon. in Jan.
                3 Presidents' Day third Mon. in Feb.
                4 Memorial Day last Mon. in May
                5 Independence Day 4-Jul
                6 Labor Day first Mon. in Sept
                7 Veterans' Day Nov. 11
                8 Thanksgiving fourth Thurs. in Nov.
                9 Fri after Thanksgiving Friday after Thanksgiving
                10 Christmas Day Dec. 25


                HTH







                share|improve this answer














                share|improve this answer



                share|improve this answer








                edited Jan 5 '15 at 22:54

























                answered Dec 31 '14 at 23:47









                BethBeth

                8,9971 gold badge16 silver badges36 bronze badges




                8,9971 gold badge16 silver badges36 bronze badges























                    2














                    This is the best I could do, still uses a loop but uses date functions instead of incrementing a minutes variable. Hope you like it.





                    --set up our source data
                    declare @business_hours table
                    (
                    work_day varchar(10),
                    open_time varchar(8),
                    close_time varchar(8)
                    )
                    insert into @business_hours values ('Monday', '08:30:00', '17:00:00')
                    insert into @business_hours values ('Tuesday', '08:30:00', '17:00:00')
                    insert into @business_hours values ('Wednesday', '08:30:00', '17:00:00')
                    insert into @business_hours values ('Thursday', '08:30:00', '17:00:00')
                    insert into @business_hours values ('Friday', '08:30:00', '18:00:00')
                    insert into @business_hours values ('Saturday', '09:00:00', '14:00:00')

                    declare @holidays table
                    (
                    holiday varchar(10)
                    )
                    insert into @holidays values ('2015-01-01')
                    insert into @holidays values ('2015-01-02')



                    --Im going to assume the SLA of 2 standard business days (0900-1700) = 8*60*2 = 960
                    declare @start_date datetime = '2014-12-31 16:12:47'
                    declare @time_span int = 960-- time till due in minutes



                    declare @true bit = 'true'
                    declare @false bit = 'false'



                    declare @due_date datetime --our output



                    --other variables
                    declare @date_string varchar(10)
                    declare @today_closing datetime
                    declare @is_workday bit = @true
                    declare @is_holiday bit = @false



                    --Given our timespan is in minutes, lets also assume we dont care about seconds in start or due dates
                    set @start_date = DATEADD(ss,datepart(ss,@start_date)*-1,@start_date)



                    while (@time_span > 0)
                    begin



                    set @due_date = DATEADD(MINUTE,@time_span,@start_date)
                    set @date_string = FORMAT(DATEADD(dd, 0, DATEDIFF(dd, 0, @start_date)),'yyyy-MM-dd')
                    set @today_closing = (select convert(datetime,@date_string + ' ' + close_time) from @business_hours where work_day = DATENAME(weekday,@start_date))

                    if exists((select work_day from @business_hours where work_day = DATENAME(weekday,@start_date)))
                    set @is_workday = @true
                    else
                    set @is_workday = @false

                    if exists(select holiday from @holidays where holiday = @date_string)
                    set @is_holiday = @true
                    else
                    set @is_holiday = @false

                    if @is_workday = @true and @is_holiday = @false
                    begin
                    if @due_date > @today_closing
                    set @time_span = @time_span - datediff(MINUTE, @start_date, @today_closing)
                    else
                    set @time_span = @time_span - datediff(minute, @start_date, @due_date)
                    end

                    set @date_string = FORMAT(DATEADD(dd, 1, DATEDIFF(dd, 0, @start_date)),'yyyy-MM-dd')
                    set @start_date = CONVERT(datetime, @date_string + ' ' + isnull((select open_time from @business_hours where work_day = DATENAME(weekday,convert(datetime,@date_string))),''))


                    end



                    select @due_date








                    share|improve this answer



























                      2














                      This is the best I could do, still uses a loop but uses date functions instead of incrementing a minutes variable. Hope you like it.





                      --set up our source data
                      declare @business_hours table
                      (
                      work_day varchar(10),
                      open_time varchar(8),
                      close_time varchar(8)
                      )
                      insert into @business_hours values ('Monday', '08:30:00', '17:00:00')
                      insert into @business_hours values ('Tuesday', '08:30:00', '17:00:00')
                      insert into @business_hours values ('Wednesday', '08:30:00', '17:00:00')
                      insert into @business_hours values ('Thursday', '08:30:00', '17:00:00')
                      insert into @business_hours values ('Friday', '08:30:00', '18:00:00')
                      insert into @business_hours values ('Saturday', '09:00:00', '14:00:00')

                      declare @holidays table
                      (
                      holiday varchar(10)
                      )
                      insert into @holidays values ('2015-01-01')
                      insert into @holidays values ('2015-01-02')



                      --Im going to assume the SLA of 2 standard business days (0900-1700) = 8*60*2 = 960
                      declare @start_date datetime = '2014-12-31 16:12:47'
                      declare @time_span int = 960-- time till due in minutes



                      declare @true bit = 'true'
                      declare @false bit = 'false'



                      declare @due_date datetime --our output



                      --other variables
                      declare @date_string varchar(10)
                      declare @today_closing datetime
                      declare @is_workday bit = @true
                      declare @is_holiday bit = @false



                      --Given our timespan is in minutes, lets also assume we dont care about seconds in start or due dates
                      set @start_date = DATEADD(ss,datepart(ss,@start_date)*-1,@start_date)



                      while (@time_span > 0)
                      begin



                      set @due_date = DATEADD(MINUTE,@time_span,@start_date)
                      set @date_string = FORMAT(DATEADD(dd, 0, DATEDIFF(dd, 0, @start_date)),'yyyy-MM-dd')
                      set @today_closing = (select convert(datetime,@date_string + ' ' + close_time) from @business_hours where work_day = DATENAME(weekday,@start_date))

                      if exists((select work_day from @business_hours where work_day = DATENAME(weekday,@start_date)))
                      set @is_workday = @true
                      else
                      set @is_workday = @false

                      if exists(select holiday from @holidays where holiday = @date_string)
                      set @is_holiday = @true
                      else
                      set @is_holiday = @false

                      if @is_workday = @true and @is_holiday = @false
                      begin
                      if @due_date > @today_closing
                      set @time_span = @time_span - datediff(MINUTE, @start_date, @today_closing)
                      else
                      set @time_span = @time_span - datediff(minute, @start_date, @due_date)
                      end

                      set @date_string = FORMAT(DATEADD(dd, 1, DATEDIFF(dd, 0, @start_date)),'yyyy-MM-dd')
                      set @start_date = CONVERT(datetime, @date_string + ' ' + isnull((select open_time from @business_hours where work_day = DATENAME(weekday,convert(datetime,@date_string))),''))


                      end



                      select @due_date








                      share|improve this answer

























                        2












                        2








                        2







                        This is the best I could do, still uses a loop but uses date functions instead of incrementing a minutes variable. Hope you like it.





                        --set up our source data
                        declare @business_hours table
                        (
                        work_day varchar(10),
                        open_time varchar(8),
                        close_time varchar(8)
                        )
                        insert into @business_hours values ('Monday', '08:30:00', '17:00:00')
                        insert into @business_hours values ('Tuesday', '08:30:00', '17:00:00')
                        insert into @business_hours values ('Wednesday', '08:30:00', '17:00:00')
                        insert into @business_hours values ('Thursday', '08:30:00', '17:00:00')
                        insert into @business_hours values ('Friday', '08:30:00', '18:00:00')
                        insert into @business_hours values ('Saturday', '09:00:00', '14:00:00')

                        declare @holidays table
                        (
                        holiday varchar(10)
                        )
                        insert into @holidays values ('2015-01-01')
                        insert into @holidays values ('2015-01-02')



                        --Im going to assume the SLA of 2 standard business days (0900-1700) = 8*60*2 = 960
                        declare @start_date datetime = '2014-12-31 16:12:47'
                        declare @time_span int = 960-- time till due in minutes



                        declare @true bit = 'true'
                        declare @false bit = 'false'



                        declare @due_date datetime --our output



                        --other variables
                        declare @date_string varchar(10)
                        declare @today_closing datetime
                        declare @is_workday bit = @true
                        declare @is_holiday bit = @false



                        --Given our timespan is in minutes, lets also assume we dont care about seconds in start or due dates
                        set @start_date = DATEADD(ss,datepart(ss,@start_date)*-1,@start_date)



                        while (@time_span > 0)
                        begin



                        set @due_date = DATEADD(MINUTE,@time_span,@start_date)
                        set @date_string = FORMAT(DATEADD(dd, 0, DATEDIFF(dd, 0, @start_date)),'yyyy-MM-dd')
                        set @today_closing = (select convert(datetime,@date_string + ' ' + close_time) from @business_hours where work_day = DATENAME(weekday,@start_date))

                        if exists((select work_day from @business_hours where work_day = DATENAME(weekday,@start_date)))
                        set @is_workday = @true
                        else
                        set @is_workday = @false

                        if exists(select holiday from @holidays where holiday = @date_string)
                        set @is_holiday = @true
                        else
                        set @is_holiday = @false

                        if @is_workday = @true and @is_holiday = @false
                        begin
                        if @due_date > @today_closing
                        set @time_span = @time_span - datediff(MINUTE, @start_date, @today_closing)
                        else
                        set @time_span = @time_span - datediff(minute, @start_date, @due_date)
                        end

                        set @date_string = FORMAT(DATEADD(dd, 1, DATEDIFF(dd, 0, @start_date)),'yyyy-MM-dd')
                        set @start_date = CONVERT(datetime, @date_string + ' ' + isnull((select open_time from @business_hours where work_day = DATENAME(weekday,convert(datetime,@date_string))),''))


                        end



                        select @due_date








                        share|improve this answer













                        This is the best I could do, still uses a loop but uses date functions instead of incrementing a minutes variable. Hope you like it.





                        --set up our source data
                        declare @business_hours table
                        (
                        work_day varchar(10),
                        open_time varchar(8),
                        close_time varchar(8)
                        )
                        insert into @business_hours values ('Monday', '08:30:00', '17:00:00')
                        insert into @business_hours values ('Tuesday', '08:30:00', '17:00:00')
                        insert into @business_hours values ('Wednesday', '08:30:00', '17:00:00')
                        insert into @business_hours values ('Thursday', '08:30:00', '17:00:00')
                        insert into @business_hours values ('Friday', '08:30:00', '18:00:00')
                        insert into @business_hours values ('Saturday', '09:00:00', '14:00:00')

                        declare @holidays table
                        (
                        holiday varchar(10)
                        )
                        insert into @holidays values ('2015-01-01')
                        insert into @holidays values ('2015-01-02')



                        --Im going to assume the SLA of 2 standard business days (0900-1700) = 8*60*2 = 960
                        declare @start_date datetime = '2014-12-31 16:12:47'
                        declare @time_span int = 960-- time till due in minutes



                        declare @true bit = 'true'
                        declare @false bit = 'false'



                        declare @due_date datetime --our output



                        --other variables
                        declare @date_string varchar(10)
                        declare @today_closing datetime
                        declare @is_workday bit = @true
                        declare @is_holiday bit = @false



                        --Given our timespan is in minutes, lets also assume we dont care about seconds in start or due dates
                        set @start_date = DATEADD(ss,datepart(ss,@start_date)*-1,@start_date)



                        while (@time_span > 0)
                        begin



                        set @due_date = DATEADD(MINUTE,@time_span,@start_date)
                        set @date_string = FORMAT(DATEADD(dd, 0, DATEDIFF(dd, 0, @start_date)),'yyyy-MM-dd')
                        set @today_closing = (select convert(datetime,@date_string + ' ' + close_time) from @business_hours where work_day = DATENAME(weekday,@start_date))

                        if exists((select work_day from @business_hours where work_day = DATENAME(weekday,@start_date)))
                        set @is_workday = @true
                        else
                        set @is_workday = @false

                        if exists(select holiday from @holidays where holiday = @date_string)
                        set @is_holiday = @true
                        else
                        set @is_holiday = @false

                        if @is_workday = @true and @is_holiday = @false
                        begin
                        if @due_date > @today_closing
                        set @time_span = @time_span - datediff(MINUTE, @start_date, @today_closing)
                        else
                        set @time_span = @time_span - datediff(minute, @start_date, @due_date)
                        end

                        set @date_string = FORMAT(DATEADD(dd, 1, DATEDIFF(dd, 0, @start_date)),'yyyy-MM-dd')
                        set @start_date = CONVERT(datetime, @date_string + ' ' + isnull((select open_time from @business_hours where work_day = DATENAME(weekday,convert(datetime,@date_string))),''))


                        end



                        select @due_date









                        share|improve this answer












                        share|improve this answer



                        share|improve this answer










                        answered Jan 1 '15 at 23:26









                        G BG B

                        1,2546 silver badges10 bronze badges




                        1,2546 silver badges10 bronze badges





















                            1














                            Here is an option using a WorkSchedule table, which will contain the business hours that are available to count towards the SLA. To account for weekends and holidays, just do not insert records for these days into the WorkSchedule table.



                            This solution also uses a "Tally" table aka numbers table in the due date calc. I also included debug output to help you see what is going on, so just comment out or uncomment any debug sections to see less/more info.



                            I used SQL temp tables in this example so that you can run it without messing up your current database schema, but you should replace with physical tables if you use this solution.



                            Test Data setups:



                            CREATE TABLE #WorkSchedule(WorkStart datetime not null primary key, WorkEnd datetime not null);
                            GO
                            CREATE TABLE #Tally (N int not null primary key);
                            GO

                            --POPULATE TEST DATA
                            --populate Tally table
                            insert into #Tally (N)
                            select top 10000 N = row_number() over(order by o.object_id)
                            from sys.objects o cross apply sys.objects o2
                            ;
                            go

                            --POPULATE WITH DUMMY TEST DATA
                            INSERT INTO #WorkSchedule(WorkStart, WorkEnd)
                            SELECT
                            workStart = dateadd(hour, 8, t.workDate)
                            , workEnd = dateadd(hour, 17, t.workDate)
                            FROM (
                            SELECT top 10000 workDate = dateadd(day, row_number() over(order by o.object_id), '2000-01-01')
                            FROM sys.objects o cross apply sys.objects o2
                            ) t
                            --Exclude weekends from work schedule
                            WHERE datename(weekday, t.workDate) not in ('Saturday','Sunday')
                            ;

                            GO


                            Code to calculate Due Date:



                            SET NOCOUNT ON;
                            DECLARE @startDate datetime;
                            DECLARE @SLA_timespan_mins int;

                            DECLARE @workStartDayOne datetime;
                            DECLARE @SLA_Adjusted int;
                            DECLARE @dueDate datetime;

                            --SET PARAM VALUES HERE FOR TESTING TO ANY DATE/SLA TIMESPAN YOU WANT:
                            SET @startDate = '2014-01-04 05:00'; --Saturday
                            SET @SLA_timespan_mins = 10 * 60 ; --10 hrs.

                            --get the info day 1, since your start date might be after the work start time.
                            select top 1 @workStartDayOne = s.WorkStart
                            --increase the SLA timespan mins to account for difference between work start and start time
                            , @SLA_Adjusted = case when @startDate > s.WorkStart then datediff(minute, s.WorkStart, @startDate) else 0 end + @SLA_timespan_mins
                            from #WorkSchedule s
                            where s.WorkEnd > @startDate
                            and s.WorkStart <> s.WorkEnd
                            order by s.WorkStart asc
                            ;

                            --DEBUG info:
                            select 'Debug Info' as DebugInfo, @startDate AS StartDate, @workStartDayOne as workStartDayOne, @SLA_timespan_mins as SLA_timespan_mins, @SLA_Adjusted as SLA_Adjusted;

                            --now sum all the non work hours during that period and determine the additional mins that need added.
                            ;with cteWorkMins as
                            (
                            SELECT TOP (@SLA_Adjusted)
                            s.WorkStart, s.WorkEnd
                            , WorkMinute = dateadd(minute, t.N, cast(s.WorkStart as datetime))
                            , t.N as MinuteOfWorkDay
                            , RowNum = row_number() over(order by s.WorkStart, t.N)
                            FROM #WorkSchedule s
                            INNER JOIN #Tally t
                            ON t.N between 1 and datediff(minute, s.WorkStart, s.WorkEnd)
                            WHERE s.WorkStart >= @workStartDayOne
                            ORDER BY s.WorkStart, t.N
                            )
                            /**/
                            SELECT @dueDate = m.WorkMinute
                            FROM cteWorkMins m
                            WHERE m.RowNum = @SLA_Adjusted
                            --*/
                            /**
                            --DEBUG: this query will show every minute that is accounted for during the Due Date calculation.
                            SELECT m.*
                            FROM cteWorkMins m
                            --WHERE m.RowNum = @SLA_Adjusted
                            ORDER BY m.WorkMinute
                            --*/
                            ;

                            select @dueDate as DueDate;
                            GO


                            Test Cleanup:



                            IF object_id('TEMPDB..#WorkSchedule') IS NOT NULL
                            DROP TABLE #WorkSchedule;
                            GO
                            IF object_id('TEMPDB..#Tally') IS NOT NULL
                            DROP TABLE #Tally;


                            GO






                            share|improve this answer



























                              1














                              Here is an option using a WorkSchedule table, which will contain the business hours that are available to count towards the SLA. To account for weekends and holidays, just do not insert records for these days into the WorkSchedule table.



                              This solution also uses a "Tally" table aka numbers table in the due date calc. I also included debug output to help you see what is going on, so just comment out or uncomment any debug sections to see less/more info.



                              I used SQL temp tables in this example so that you can run it without messing up your current database schema, but you should replace with physical tables if you use this solution.



                              Test Data setups:



                              CREATE TABLE #WorkSchedule(WorkStart datetime not null primary key, WorkEnd datetime not null);
                              GO
                              CREATE TABLE #Tally (N int not null primary key);
                              GO

                              --POPULATE TEST DATA
                              --populate Tally table
                              insert into #Tally (N)
                              select top 10000 N = row_number() over(order by o.object_id)
                              from sys.objects o cross apply sys.objects o2
                              ;
                              go

                              --POPULATE WITH DUMMY TEST DATA
                              INSERT INTO #WorkSchedule(WorkStart, WorkEnd)
                              SELECT
                              workStart = dateadd(hour, 8, t.workDate)
                              , workEnd = dateadd(hour, 17, t.workDate)
                              FROM (
                              SELECT top 10000 workDate = dateadd(day, row_number() over(order by o.object_id), '2000-01-01')
                              FROM sys.objects o cross apply sys.objects o2
                              ) t
                              --Exclude weekends from work schedule
                              WHERE datename(weekday, t.workDate) not in ('Saturday','Sunday')
                              ;

                              GO


                              Code to calculate Due Date:



                              SET NOCOUNT ON;
                              DECLARE @startDate datetime;
                              DECLARE @SLA_timespan_mins int;

                              DECLARE @workStartDayOne datetime;
                              DECLARE @SLA_Adjusted int;
                              DECLARE @dueDate datetime;

                              --SET PARAM VALUES HERE FOR TESTING TO ANY DATE/SLA TIMESPAN YOU WANT:
                              SET @startDate = '2014-01-04 05:00'; --Saturday
                              SET @SLA_timespan_mins = 10 * 60 ; --10 hrs.

                              --get the info day 1, since your start date might be after the work start time.
                              select top 1 @workStartDayOne = s.WorkStart
                              --increase the SLA timespan mins to account for difference between work start and start time
                              , @SLA_Adjusted = case when @startDate > s.WorkStart then datediff(minute, s.WorkStart, @startDate) else 0 end + @SLA_timespan_mins
                              from #WorkSchedule s
                              where s.WorkEnd > @startDate
                              and s.WorkStart <> s.WorkEnd
                              order by s.WorkStart asc
                              ;

                              --DEBUG info:
                              select 'Debug Info' as DebugInfo, @startDate AS StartDate, @workStartDayOne as workStartDayOne, @SLA_timespan_mins as SLA_timespan_mins, @SLA_Adjusted as SLA_Adjusted;

                              --now sum all the non work hours during that period and determine the additional mins that need added.
                              ;with cteWorkMins as
                              (
                              SELECT TOP (@SLA_Adjusted)
                              s.WorkStart, s.WorkEnd
                              , WorkMinute = dateadd(minute, t.N, cast(s.WorkStart as datetime))
                              , t.N as MinuteOfWorkDay
                              , RowNum = row_number() over(order by s.WorkStart, t.N)
                              FROM #WorkSchedule s
                              INNER JOIN #Tally t
                              ON t.N between 1 and datediff(minute, s.WorkStart, s.WorkEnd)
                              WHERE s.WorkStart >= @workStartDayOne
                              ORDER BY s.WorkStart, t.N
                              )
                              /**/
                              SELECT @dueDate = m.WorkMinute
                              FROM cteWorkMins m
                              WHERE m.RowNum = @SLA_Adjusted
                              --*/
                              /**
                              --DEBUG: this query will show every minute that is accounted for during the Due Date calculation.
                              SELECT m.*
                              FROM cteWorkMins m
                              --WHERE m.RowNum = @SLA_Adjusted
                              ORDER BY m.WorkMinute
                              --*/
                              ;

                              select @dueDate as DueDate;
                              GO


                              Test Cleanup:



                              IF object_id('TEMPDB..#WorkSchedule') IS NOT NULL
                              DROP TABLE #WorkSchedule;
                              GO
                              IF object_id('TEMPDB..#Tally') IS NOT NULL
                              DROP TABLE #Tally;


                              GO






                              share|improve this answer

























                                1












                                1








                                1







                                Here is an option using a WorkSchedule table, which will contain the business hours that are available to count towards the SLA. To account for weekends and holidays, just do not insert records for these days into the WorkSchedule table.



                                This solution also uses a "Tally" table aka numbers table in the due date calc. I also included debug output to help you see what is going on, so just comment out or uncomment any debug sections to see less/more info.



                                I used SQL temp tables in this example so that you can run it without messing up your current database schema, but you should replace with physical tables if you use this solution.



                                Test Data setups:



                                CREATE TABLE #WorkSchedule(WorkStart datetime not null primary key, WorkEnd datetime not null);
                                GO
                                CREATE TABLE #Tally (N int not null primary key);
                                GO

                                --POPULATE TEST DATA
                                --populate Tally table
                                insert into #Tally (N)
                                select top 10000 N = row_number() over(order by o.object_id)
                                from sys.objects o cross apply sys.objects o2
                                ;
                                go

                                --POPULATE WITH DUMMY TEST DATA
                                INSERT INTO #WorkSchedule(WorkStart, WorkEnd)
                                SELECT
                                workStart = dateadd(hour, 8, t.workDate)
                                , workEnd = dateadd(hour, 17, t.workDate)
                                FROM (
                                SELECT top 10000 workDate = dateadd(day, row_number() over(order by o.object_id), '2000-01-01')
                                FROM sys.objects o cross apply sys.objects o2
                                ) t
                                --Exclude weekends from work schedule
                                WHERE datename(weekday, t.workDate) not in ('Saturday','Sunday')
                                ;

                                GO


                                Code to calculate Due Date:



                                SET NOCOUNT ON;
                                DECLARE @startDate datetime;
                                DECLARE @SLA_timespan_mins int;

                                DECLARE @workStartDayOne datetime;
                                DECLARE @SLA_Adjusted int;
                                DECLARE @dueDate datetime;

                                --SET PARAM VALUES HERE FOR TESTING TO ANY DATE/SLA TIMESPAN YOU WANT:
                                SET @startDate = '2014-01-04 05:00'; --Saturday
                                SET @SLA_timespan_mins = 10 * 60 ; --10 hrs.

                                --get the info day 1, since your start date might be after the work start time.
                                select top 1 @workStartDayOne = s.WorkStart
                                --increase the SLA timespan mins to account for difference between work start and start time
                                , @SLA_Adjusted = case when @startDate > s.WorkStart then datediff(minute, s.WorkStart, @startDate) else 0 end + @SLA_timespan_mins
                                from #WorkSchedule s
                                where s.WorkEnd > @startDate
                                and s.WorkStart <> s.WorkEnd
                                order by s.WorkStart asc
                                ;

                                --DEBUG info:
                                select 'Debug Info' as DebugInfo, @startDate AS StartDate, @workStartDayOne as workStartDayOne, @SLA_timespan_mins as SLA_timespan_mins, @SLA_Adjusted as SLA_Adjusted;

                                --now sum all the non work hours during that period and determine the additional mins that need added.
                                ;with cteWorkMins as
                                (
                                SELECT TOP (@SLA_Adjusted)
                                s.WorkStart, s.WorkEnd
                                , WorkMinute = dateadd(minute, t.N, cast(s.WorkStart as datetime))
                                , t.N as MinuteOfWorkDay
                                , RowNum = row_number() over(order by s.WorkStart, t.N)
                                FROM #WorkSchedule s
                                INNER JOIN #Tally t
                                ON t.N between 1 and datediff(minute, s.WorkStart, s.WorkEnd)
                                WHERE s.WorkStart >= @workStartDayOne
                                ORDER BY s.WorkStart, t.N
                                )
                                /**/
                                SELECT @dueDate = m.WorkMinute
                                FROM cteWorkMins m
                                WHERE m.RowNum = @SLA_Adjusted
                                --*/
                                /**
                                --DEBUG: this query will show every minute that is accounted for during the Due Date calculation.
                                SELECT m.*
                                FROM cteWorkMins m
                                --WHERE m.RowNum = @SLA_Adjusted
                                ORDER BY m.WorkMinute
                                --*/
                                ;

                                select @dueDate as DueDate;
                                GO


                                Test Cleanup:



                                IF object_id('TEMPDB..#WorkSchedule') IS NOT NULL
                                DROP TABLE #WorkSchedule;
                                GO
                                IF object_id('TEMPDB..#Tally') IS NOT NULL
                                DROP TABLE #Tally;


                                GO






                                share|improve this answer













                                Here is an option using a WorkSchedule table, which will contain the business hours that are available to count towards the SLA. To account for weekends and holidays, just do not insert records for these days into the WorkSchedule table.



                                This solution also uses a "Tally" table aka numbers table in the due date calc. I also included debug output to help you see what is going on, so just comment out or uncomment any debug sections to see less/more info.



                                I used SQL temp tables in this example so that you can run it without messing up your current database schema, but you should replace with physical tables if you use this solution.



                                Test Data setups:



                                CREATE TABLE #WorkSchedule(WorkStart datetime not null primary key, WorkEnd datetime not null);
                                GO
                                CREATE TABLE #Tally (N int not null primary key);
                                GO

                                --POPULATE TEST DATA
                                --populate Tally table
                                insert into #Tally (N)
                                select top 10000 N = row_number() over(order by o.object_id)
                                from sys.objects o cross apply sys.objects o2
                                ;
                                go

                                --POPULATE WITH DUMMY TEST DATA
                                INSERT INTO #WorkSchedule(WorkStart, WorkEnd)
                                SELECT
                                workStart = dateadd(hour, 8, t.workDate)
                                , workEnd = dateadd(hour, 17, t.workDate)
                                FROM (
                                SELECT top 10000 workDate = dateadd(day, row_number() over(order by o.object_id), '2000-01-01')
                                FROM sys.objects o cross apply sys.objects o2
                                ) t
                                --Exclude weekends from work schedule
                                WHERE datename(weekday, t.workDate) not in ('Saturday','Sunday')
                                ;

                                GO


                                Code to calculate Due Date:



                                SET NOCOUNT ON;
                                DECLARE @startDate datetime;
                                DECLARE @SLA_timespan_mins int;

                                DECLARE @workStartDayOne datetime;
                                DECLARE @SLA_Adjusted int;
                                DECLARE @dueDate datetime;

                                --SET PARAM VALUES HERE FOR TESTING TO ANY DATE/SLA TIMESPAN YOU WANT:
                                SET @startDate = '2014-01-04 05:00'; --Saturday
                                SET @SLA_timespan_mins = 10 * 60 ; --10 hrs.

                                --get the info day 1, since your start date might be after the work start time.
                                select top 1 @workStartDayOne = s.WorkStart
                                --increase the SLA timespan mins to account for difference between work start and start time
                                , @SLA_Adjusted = case when @startDate > s.WorkStart then datediff(minute, s.WorkStart, @startDate) else 0 end + @SLA_timespan_mins
                                from #WorkSchedule s
                                where s.WorkEnd > @startDate
                                and s.WorkStart <> s.WorkEnd
                                order by s.WorkStart asc
                                ;

                                --DEBUG info:
                                select 'Debug Info' as DebugInfo, @startDate AS StartDate, @workStartDayOne as workStartDayOne, @SLA_timespan_mins as SLA_timespan_mins, @SLA_Adjusted as SLA_Adjusted;

                                --now sum all the non work hours during that period and determine the additional mins that need added.
                                ;with cteWorkMins as
                                (
                                SELECT TOP (@SLA_Adjusted)
                                s.WorkStart, s.WorkEnd
                                , WorkMinute = dateadd(minute, t.N, cast(s.WorkStart as datetime))
                                , t.N as MinuteOfWorkDay
                                , RowNum = row_number() over(order by s.WorkStart, t.N)
                                FROM #WorkSchedule s
                                INNER JOIN #Tally t
                                ON t.N between 1 and datediff(minute, s.WorkStart, s.WorkEnd)
                                WHERE s.WorkStart >= @workStartDayOne
                                ORDER BY s.WorkStart, t.N
                                )
                                /**/
                                SELECT @dueDate = m.WorkMinute
                                FROM cteWorkMins m
                                WHERE m.RowNum = @SLA_Adjusted
                                --*/
                                /**
                                --DEBUG: this query will show every minute that is accounted for during the Due Date calculation.
                                SELECT m.*
                                FROM cteWorkMins m
                                --WHERE m.RowNum = @SLA_Adjusted
                                ORDER BY m.WorkMinute
                                --*/
                                ;

                                select @dueDate as DueDate;
                                GO


                                Test Cleanup:



                                IF object_id('TEMPDB..#WorkSchedule') IS NOT NULL
                                DROP TABLE #WorkSchedule;
                                GO
                                IF object_id('TEMPDB..#Tally') IS NOT NULL
                                DROP TABLE #Tally;


                                GO







                                share|improve this answer












                                share|improve this answer



                                share|improve this answer










                                answered Jan 3 '15 at 17:52









                                BateTechBateTech

                                4,4573 gold badges15 silver badges30 bronze badges




                                4,4573 gold badges15 silver badges30 bronze badges





















                                    1














                                    as I understood from your question, what you need is as follow



                                    1. You have given start date and number of minutes added to it, then you need to get the due date

                                    2. To get the due date, you need to exclude the holidays and the due date should be during business day

                                    here is what you can do



                                    declare @start datetime,
                                    @min int,
                                    @days int

                                    set @start= '28 Dec 2014'
                                    set @min = 2200

                                    -- get the number of days
                                    set @days=datediff(day,@start,dateadd(minute,@min,@start))

                                    -- get the due date
                                    select max(Date)
                                    from
                                    (select row_number() over( order by t.Id)-1 as Id,t.Date
                                    from DateMetadata t
                                    inner join BusinessDays b on Day(t.Date) = b.Day
                                    where t.Date > = @start and not exists(select 1 from Holidays h
                                    where h.Day=Day(t.Date)
                                    and h.Month=Month(t.Date))) as p
                                    where p.Id < @days


                                    Note :that DateMetadata table you will setup it in your database once



                                    the setup for the above code :



                                    create table Holidays(Id int identity(1,1),
                                    Name nvarchar(50),
                                    Day int,
                                    Month int)
                                    create table BusinessDays(Id int identity(1,1),
                                    Name nvarchar(20),
                                    Day int)

                                    -- i am putting some days that are known,
                                    -- it depends on you to define which holidays you want
                                    insert into Holidays (Name,Day,Month) values('Christmas',25,12)
                                    insert into Holidays(Name,Day,Month) values('New Year',31,12)
                                    insert into Holidays(Name,Day,Month) values('Valentine',14,2)
                                    insert into Holidays(Name,Day,Month) values('Mothers day',21,3)
                                    insert into Holidays(Name,Day,Month) values('April fools day',1,4)

                                    -- i am assuming that the business days are from monday till friday and
                                    -- saturday and sunday are off days
                                    insert into BusinessDays(Name,Day) values ('Monday',1)
                                    insert into BusinessDays(Name,Day) values('Tuesday',2)
                                    insert into BusinessDays(Name,Day) values('Wednesday',3)
                                    insert into BusinessDays(Name,Day) values('Thursday',4)
                                    insert into BusinessDays(Name,Day) values('Friday',5)


                                    this table is needed and you will setup it once



                                    -- set up a table that contains all dates from now till 2050 for example
                                    -- and you can change the value of 2050 depending on your needs
                                    -- this table you will setup it once
                                    create table DateMetadata(Id int identity(1,1),
                                    Date datetime)

                                    declare @date datetime
                                    set @date='01 Jan 2014'
                                    while @date < '31 Dec 2050'
                                    begin
                                    insert into DateMetadata(Date) values(@date)
                                    set @date = @date + 1
                                    end


                                    here a working DEMO



                                    if you need any explanation, i am ready



                                    hope it will help you






                                    share|improve this answer


















                                    • 1





                                      That is not how holidays work. Some of them, yes, but not for anything that changes per year (i.e. Thanksgiving = 4th Thursday in November, and July 4th could be observed on either the 3rd or 5th if the 4th is on a Saturday or Sunday, respectively).

                                      – Solomon Rutzky
                                      Jan 6 '15 at 18:31






                                    • 1





                                      @srutzky at the end it depends on the country he is from and the table i defined is somehow flexible to define any holiday he wants independent from the year, i looked to the calendar and i picked some

                                      – Monah
                                      Jan 6 '15 at 18:38
















                                    1














                                    as I understood from your question, what you need is as follow



                                    1. You have given start date and number of minutes added to it, then you need to get the due date

                                    2. To get the due date, you need to exclude the holidays and the due date should be during business day

                                    here is what you can do



                                    declare @start datetime,
                                    @min int,
                                    @days int

                                    set @start= '28 Dec 2014'
                                    set @min = 2200

                                    -- get the number of days
                                    set @days=datediff(day,@start,dateadd(minute,@min,@start))

                                    -- get the due date
                                    select max(Date)
                                    from
                                    (select row_number() over( order by t.Id)-1 as Id,t.Date
                                    from DateMetadata t
                                    inner join BusinessDays b on Day(t.Date) = b.Day
                                    where t.Date > = @start and not exists(select 1 from Holidays h
                                    where h.Day=Day(t.Date)
                                    and h.Month=Month(t.Date))) as p
                                    where p.Id < @days


                                    Note :that DateMetadata table you will setup it in your database once



                                    the setup for the above code :



                                    create table Holidays(Id int identity(1,1),
                                    Name nvarchar(50),
                                    Day int,
                                    Month int)
                                    create table BusinessDays(Id int identity(1,1),
                                    Name nvarchar(20),
                                    Day int)

                                    -- i am putting some days that are known,
                                    -- it depends on you to define which holidays you want
                                    insert into Holidays (Name,Day,Month) values('Christmas',25,12)
                                    insert into Holidays(Name,Day,Month) values('New Year',31,12)
                                    insert into Holidays(Name,Day,Month) values('Valentine',14,2)
                                    insert into Holidays(Name,Day,Month) values('Mothers day',21,3)
                                    insert into Holidays(Name,Day,Month) values('April fools day',1,4)

                                    -- i am assuming that the business days are from monday till friday and
                                    -- saturday and sunday are off days
                                    insert into BusinessDays(Name,Day) values ('Monday',1)
                                    insert into BusinessDays(Name,Day) values('Tuesday',2)
                                    insert into BusinessDays(Name,Day) values('Wednesday',3)
                                    insert into BusinessDays(Name,Day) values('Thursday',4)
                                    insert into BusinessDays(Name,Day) values('Friday',5)


                                    this table is needed and you will setup it once



                                    -- set up a table that contains all dates from now till 2050 for example
                                    -- and you can change the value of 2050 depending on your needs
                                    -- this table you will setup it once
                                    create table DateMetadata(Id int identity(1,1),
                                    Date datetime)

                                    declare @date datetime
                                    set @date='01 Jan 2014'
                                    while @date < '31 Dec 2050'
                                    begin
                                    insert into DateMetadata(Date) values(@date)
                                    set @date = @date + 1
                                    end


                                    here a working DEMO



                                    if you need any explanation, i am ready



                                    hope it will help you






                                    share|improve this answer


















                                    • 1





                                      That is not how holidays work. Some of them, yes, but not for anything that changes per year (i.e. Thanksgiving = 4th Thursday in November, and July 4th could be observed on either the 3rd or 5th if the 4th is on a Saturday or Sunday, respectively).

                                      – Solomon Rutzky
                                      Jan 6 '15 at 18:31






                                    • 1





                                      @srutzky at the end it depends on the country he is from and the table i defined is somehow flexible to define any holiday he wants independent from the year, i looked to the calendar and i picked some

                                      – Monah
                                      Jan 6 '15 at 18:38














                                    1












                                    1








                                    1







                                    as I understood from your question, what you need is as follow



                                    1. You have given start date and number of minutes added to it, then you need to get the due date

                                    2. To get the due date, you need to exclude the holidays and the due date should be during business day

                                    here is what you can do



                                    declare @start datetime,
                                    @min int,
                                    @days int

                                    set @start= '28 Dec 2014'
                                    set @min = 2200

                                    -- get the number of days
                                    set @days=datediff(day,@start,dateadd(minute,@min,@start))

                                    -- get the due date
                                    select max(Date)
                                    from
                                    (select row_number() over( order by t.Id)-1 as Id,t.Date
                                    from DateMetadata t
                                    inner join BusinessDays b on Day(t.Date) = b.Day
                                    where t.Date > = @start and not exists(select 1 from Holidays h
                                    where h.Day=Day(t.Date)
                                    and h.Month=Month(t.Date))) as p
                                    where p.Id < @days


                                    Note :that DateMetadata table you will setup it in your database once



                                    the setup for the above code :



                                    create table Holidays(Id int identity(1,1),
                                    Name nvarchar(50),
                                    Day int,
                                    Month int)
                                    create table BusinessDays(Id int identity(1,1),
                                    Name nvarchar(20),
                                    Day int)

                                    -- i am putting some days that are known,
                                    -- it depends on you to define which holidays you want
                                    insert into Holidays (Name,Day,Month) values('Christmas',25,12)
                                    insert into Holidays(Name,Day,Month) values('New Year',31,12)
                                    insert into Holidays(Name,Day,Month) values('Valentine',14,2)
                                    insert into Holidays(Name,Day,Month) values('Mothers day',21,3)
                                    insert into Holidays(Name,Day,Month) values('April fools day',1,4)

                                    -- i am assuming that the business days are from monday till friday and
                                    -- saturday and sunday are off days
                                    insert into BusinessDays(Name,Day) values ('Monday',1)
                                    insert into BusinessDays(Name,Day) values('Tuesday',2)
                                    insert into BusinessDays(Name,Day) values('Wednesday',3)
                                    insert into BusinessDays(Name,Day) values('Thursday',4)
                                    insert into BusinessDays(Name,Day) values('Friday',5)


                                    this table is needed and you will setup it once



                                    -- set up a table that contains all dates from now till 2050 for example
                                    -- and you can change the value of 2050 depending on your needs
                                    -- this table you will setup it once
                                    create table DateMetadata(Id int identity(1,1),
                                    Date datetime)

                                    declare @date datetime
                                    set @date='01 Jan 2014'
                                    while @date < '31 Dec 2050'
                                    begin
                                    insert into DateMetadata(Date) values(@date)
                                    set @date = @date + 1
                                    end


                                    here a working DEMO



                                    if you need any explanation, i am ready



                                    hope it will help you






                                    share|improve this answer













                                    as I understood from your question, what you need is as follow



                                    1. You have given start date and number of minutes added to it, then you need to get the due date

                                    2. To get the due date, you need to exclude the holidays and the due date should be during business day

                                    here is what you can do



                                    declare @start datetime,
                                    @min int,
                                    @days int

                                    set @start= '28 Dec 2014'
                                    set @min = 2200

                                    -- get the number of days
                                    set @days=datediff(day,@start,dateadd(minute,@min,@start))

                                    -- get the due date
                                    select max(Date)
                                    from
                                    (select row_number() over( order by t.Id)-1 as Id,t.Date
                                    from DateMetadata t
                                    inner join BusinessDays b on Day(t.Date) = b.Day
                                    where t.Date > = @start and not exists(select 1 from Holidays h
                                    where h.Day=Day(t.Date)
                                    and h.Month=Month(t.Date))) as p
                                    where p.Id < @days


                                    Note :that DateMetadata table you will setup it in your database once



                                    the setup for the above code :



                                    create table Holidays(Id int identity(1,1),
                                    Name nvarchar(50),
                                    Day int,
                                    Month int)
                                    create table BusinessDays(Id int identity(1,1),
                                    Name nvarchar(20),
                                    Day int)

                                    -- i am putting some days that are known,
                                    -- it depends on you to define which holidays you want
                                    insert into Holidays (Name,Day,Month) values('Christmas',25,12)
                                    insert into Holidays(Name,Day,Month) values('New Year',31,12)
                                    insert into Holidays(Name,Day,Month) values('Valentine',14,2)
                                    insert into Holidays(Name,Day,Month) values('Mothers day',21,3)
                                    insert into Holidays(Name,Day,Month) values('April fools day',1,4)

                                    -- i am assuming that the business days are from monday till friday and
                                    -- saturday and sunday are off days
                                    insert into BusinessDays(Name,Day) values ('Monday',1)
                                    insert into BusinessDays(Name,Day) values('Tuesday',2)
                                    insert into BusinessDays(Name,Day) values('Wednesday',3)
                                    insert into BusinessDays(Name,Day) values('Thursday',4)
                                    insert into BusinessDays(Name,Day) values('Friday',5)


                                    this table is needed and you will setup it once



                                    -- set up a table that contains all dates from now till 2050 for example
                                    -- and you can change the value of 2050 depending on your needs
                                    -- this table you will setup it once
                                    create table DateMetadata(Id int identity(1,1),
                                    Date datetime)

                                    declare @date datetime
                                    set @date='01 Jan 2014'
                                    while @date < '31 Dec 2050'
                                    begin
                                    insert into DateMetadata(Date) values(@date)
                                    set @date = @date + 1
                                    end


                                    here a working DEMO



                                    if you need any explanation, i am ready



                                    hope it will help you







                                    share|improve this answer












                                    share|improve this answer



                                    share|improve this answer










                                    answered Jan 6 '15 at 18:02









                                    MonahMonah

                                    5,4705 gold badges18 silver badges48 bronze badges




                                    5,4705 gold badges18 silver badges48 bronze badges







                                    • 1





                                      That is not how holidays work. Some of them, yes, but not for anything that changes per year (i.e. Thanksgiving = 4th Thursday in November, and July 4th could be observed on either the 3rd or 5th if the 4th is on a Saturday or Sunday, respectively).

                                      – Solomon Rutzky
                                      Jan 6 '15 at 18:31






                                    • 1





                                      @srutzky at the end it depends on the country he is from and the table i defined is somehow flexible to define any holiday he wants independent from the year, i looked to the calendar and i picked some

                                      – Monah
                                      Jan 6 '15 at 18:38













                                    • 1





                                      That is not how holidays work. Some of them, yes, but not for anything that changes per year (i.e. Thanksgiving = 4th Thursday in November, and July 4th could be observed on either the 3rd or 5th if the 4th is on a Saturday or Sunday, respectively).

                                      – Solomon Rutzky
                                      Jan 6 '15 at 18:31






                                    • 1





                                      @srutzky at the end it depends on the country he is from and the table i defined is somehow flexible to define any holiday he wants independent from the year, i looked to the calendar and i picked some

                                      – Monah
                                      Jan 6 '15 at 18:38








                                    1




                                    1





                                    That is not how holidays work. Some of them, yes, but not for anything that changes per year (i.e. Thanksgiving = 4th Thursday in November, and July 4th could be observed on either the 3rd or 5th if the 4th is on a Saturday or Sunday, respectively).

                                    – Solomon Rutzky
                                    Jan 6 '15 at 18:31





                                    That is not how holidays work. Some of them, yes, but not for anything that changes per year (i.e. Thanksgiving = 4th Thursday in November, and July 4th could be observed on either the 3rd or 5th if the 4th is on a Saturday or Sunday, respectively).

                                    – Solomon Rutzky
                                    Jan 6 '15 at 18:31




                                    1




                                    1





                                    @srutzky at the end it depends on the country he is from and the table i defined is somehow flexible to define any holiday he wants independent from the year, i looked to the calendar and i picked some

                                    – Monah
                                    Jan 6 '15 at 18:38






                                    @srutzky at the end it depends on the country he is from and the table i defined is somehow flexible to define any holiday he wants independent from the year, i looked to the calendar and i picked some

                                    – Monah
                                    Jan 6 '15 at 18:38












                                    1














                                    Sql to Calculate due date excluding holidays and considering business hour as below :- < note : - It's working correctly Business hour (8-5) Maintain holiday table



                                    CREATE TABLE [dbo].[holiday](
                                    [id] [int] IDENTITY(1,1) NOT NULL,
                                    [region] [nvarchar](10) NULL,
                                    [Hdate] [date] NULL,
                                    )


                                    >



                                    declare @start datetime= getdate()
                                    declare @slamins int =960 --- SLA time in mins
                                    declare @Country varchar(2)='NA'
                                    declare @start_hour int = 8 -- business start hour
                                    declare @end_hour int = 17 -- business end hour
                                    declare @true bit = 'true'
                                    declare @false bit = 'false'
                                    declare @is_workday bit = @true
                                    declare @is_holiday bit = @false
                                    declare @due_date datetime
                                    declare @today_closing datetime
                                    declare @temp int = 0
                                    declare @holidays table (HDate DateTime) -- Table variable to hold holidayes

                                    ---- Get country holidays from table based on the country code (Feel free to remove this or modify as per your DB schema)
                                    Insert Into @Holidays (HDate) Select date from HOLIDAY Where region=@Country and Hdate>=DateAdd(dd, DateDiff(dd,0,@start), 0)

                                    --check for weekends
                                    set @start = case(datepart(dw,@start)+@@datefirst-1)%7
                                    when 0 then Convert(dateTime,CONVERT(varchar,@start+1,101)+' 08:00:00')
                                    when 6 then Convert(dateTime,CONVERT(varchar,@start+2,101)+' 08:00:00')
                                    else @start end

                                    -- check if start time is before business hour
                                    if datepart(hh, @start) < @start_hour set @start = Convert(dateTime,CONVERT(varchar,@start,101)+' 08:00:00')

                                    -- check if start time is after business hour
                                    if datepart(hh, @start) >= @end_hour set @start = Convert(dateTime,CONVERT(varchar,@start+1,101)+' 08:00:00')

                                    -- loop start
                                    while (@slamins > 0)
                                    begin
                                    -- prepared closing date time based on start date
                                    set @today_closing = Convert(dateTime,CONVERT(varchar,@start,101)+' 17:00:00')
                                    set @due_date = @start
                                    -- calculate number of Minute between start date and closing date
                                    set @temp = DATEDIFF(N, @start , @today_closing);

                                    --check for weekends
                                    if (DATEPART(dw, @start)!=1 AND DATEPART(dw, @start)!=7)
                                    set @is_workday = @true
                                    else
                                    set @is_workday = @false
                                    --check for holidays
                                    if (Select Count(*) From @Holidays Where HDATE=DateAdd(dd, DateDiff(dd,0,@start), 0)) = 0
                                    set @is_holiday =@false
                                    else
                                    set @is_holiday = @true
                                    if @is_workday = @true and @is_holiday = @false
                                    begin

                                    if(@temp < @slamins)
                                    begin
                                    set @slamins = @slamins - @temp
                                    end
                                    else
                                    begin
                                    set @due_date = DATEADD(MINUTE,@slamins,@start)
                                    set @slamins = 0
                                    print @due_date
                                    end
                                    end
                                    set @start = Convert(dateTime,CONVERT(varchar,@start+1,101)+' 08:00:00')
                                    end

                                    select @due_date





                                    share|improve this answer



























                                      1














                                      Sql to Calculate due date excluding holidays and considering business hour as below :- < note : - It's working correctly Business hour (8-5) Maintain holiday table



                                      CREATE TABLE [dbo].[holiday](
                                      [id] [int] IDENTITY(1,1) NOT NULL,
                                      [region] [nvarchar](10) NULL,
                                      [Hdate] [date] NULL,
                                      )


                                      >



                                      declare @start datetime= getdate()
                                      declare @slamins int =960 --- SLA time in mins
                                      declare @Country varchar(2)='NA'
                                      declare @start_hour int = 8 -- business start hour
                                      declare @end_hour int = 17 -- business end hour
                                      declare @true bit = 'true'
                                      declare @false bit = 'false'
                                      declare @is_workday bit = @true
                                      declare @is_holiday bit = @false
                                      declare @due_date datetime
                                      declare @today_closing datetime
                                      declare @temp int = 0
                                      declare @holidays table (HDate DateTime) -- Table variable to hold holidayes

                                      ---- Get country holidays from table based on the country code (Feel free to remove this or modify as per your DB schema)
                                      Insert Into @Holidays (HDate) Select date from HOLIDAY Where region=@Country and Hdate>=DateAdd(dd, DateDiff(dd,0,@start), 0)

                                      --check for weekends
                                      set @start = case(datepart(dw,@start)+@@datefirst-1)%7
                                      when 0 then Convert(dateTime,CONVERT(varchar,@start+1,101)+' 08:00:00')
                                      when 6 then Convert(dateTime,CONVERT(varchar,@start+2,101)+' 08:00:00')
                                      else @start end

                                      -- check if start time is before business hour
                                      if datepart(hh, @start) < @start_hour set @start = Convert(dateTime,CONVERT(varchar,@start,101)+' 08:00:00')

                                      -- check if start time is after business hour
                                      if datepart(hh, @start) >= @end_hour set @start = Convert(dateTime,CONVERT(varchar,@start+1,101)+' 08:00:00')

                                      -- loop start
                                      while (@slamins > 0)
                                      begin
                                      -- prepared closing date time based on start date
                                      set @today_closing = Convert(dateTime,CONVERT(varchar,@start,101)+' 17:00:00')
                                      set @due_date = @start
                                      -- calculate number of Minute between start date and closing date
                                      set @temp = DATEDIFF(N, @start , @today_closing);

                                      --check for weekends
                                      if (DATEPART(dw, @start)!=1 AND DATEPART(dw, @start)!=7)
                                      set @is_workday = @true
                                      else
                                      set @is_workday = @false
                                      --check for holidays
                                      if (Select Count(*) From @Holidays Where HDATE=DateAdd(dd, DateDiff(dd,0,@start), 0)) = 0
                                      set @is_holiday =@false
                                      else
                                      set @is_holiday = @true
                                      if @is_workday = @true and @is_holiday = @false
                                      begin

                                      if(@temp < @slamins)
                                      begin
                                      set @slamins = @slamins - @temp
                                      end
                                      else
                                      begin
                                      set @due_date = DATEADD(MINUTE,@slamins,@start)
                                      set @slamins = 0
                                      print @due_date
                                      end
                                      end
                                      set @start = Convert(dateTime,CONVERT(varchar,@start+1,101)+' 08:00:00')
                                      end

                                      select @due_date





                                      share|improve this answer

























                                        1












                                        1








                                        1







                                        Sql to Calculate due date excluding holidays and considering business hour as below :- < note : - It's working correctly Business hour (8-5) Maintain holiday table



                                        CREATE TABLE [dbo].[holiday](
                                        [id] [int] IDENTITY(1,1) NOT NULL,
                                        [region] [nvarchar](10) NULL,
                                        [Hdate] [date] NULL,
                                        )


                                        >



                                        declare @start datetime= getdate()
                                        declare @slamins int =960 --- SLA time in mins
                                        declare @Country varchar(2)='NA'
                                        declare @start_hour int = 8 -- business start hour
                                        declare @end_hour int = 17 -- business end hour
                                        declare @true bit = 'true'
                                        declare @false bit = 'false'
                                        declare @is_workday bit = @true
                                        declare @is_holiday bit = @false
                                        declare @due_date datetime
                                        declare @today_closing datetime
                                        declare @temp int = 0
                                        declare @holidays table (HDate DateTime) -- Table variable to hold holidayes

                                        ---- Get country holidays from table based on the country code (Feel free to remove this or modify as per your DB schema)
                                        Insert Into @Holidays (HDate) Select date from HOLIDAY Where region=@Country and Hdate>=DateAdd(dd, DateDiff(dd,0,@start), 0)

                                        --check for weekends
                                        set @start = case(datepart(dw,@start)+@@datefirst-1)%7
                                        when 0 then Convert(dateTime,CONVERT(varchar,@start+1,101)+' 08:00:00')
                                        when 6 then Convert(dateTime,CONVERT(varchar,@start+2,101)+' 08:00:00')
                                        else @start end

                                        -- check if start time is before business hour
                                        if datepart(hh, @start) < @start_hour set @start = Convert(dateTime,CONVERT(varchar,@start,101)+' 08:00:00')

                                        -- check if start time is after business hour
                                        if datepart(hh, @start) >= @end_hour set @start = Convert(dateTime,CONVERT(varchar,@start+1,101)+' 08:00:00')

                                        -- loop start
                                        while (@slamins > 0)
                                        begin
                                        -- prepared closing date time based on start date
                                        set @today_closing = Convert(dateTime,CONVERT(varchar,@start,101)+' 17:00:00')
                                        set @due_date = @start
                                        -- calculate number of Minute between start date and closing date
                                        set @temp = DATEDIFF(N, @start , @today_closing);

                                        --check for weekends
                                        if (DATEPART(dw, @start)!=1 AND DATEPART(dw, @start)!=7)
                                        set @is_workday = @true
                                        else
                                        set @is_workday = @false
                                        --check for holidays
                                        if (Select Count(*) From @Holidays Where HDATE=DateAdd(dd, DateDiff(dd,0,@start), 0)) = 0
                                        set @is_holiday =@false
                                        else
                                        set @is_holiday = @true
                                        if @is_workday = @true and @is_holiday = @false
                                        begin

                                        if(@temp < @slamins)
                                        begin
                                        set @slamins = @slamins - @temp
                                        end
                                        else
                                        begin
                                        set @due_date = DATEADD(MINUTE,@slamins,@start)
                                        set @slamins = 0
                                        print @due_date
                                        end
                                        end
                                        set @start = Convert(dateTime,CONVERT(varchar,@start+1,101)+' 08:00:00')
                                        end

                                        select @due_date





                                        share|improve this answer













                                        Sql to Calculate due date excluding holidays and considering business hour as below :- < note : - It's working correctly Business hour (8-5) Maintain holiday table



                                        CREATE TABLE [dbo].[holiday](
                                        [id] [int] IDENTITY(1,1) NOT NULL,
                                        [region] [nvarchar](10) NULL,
                                        [Hdate] [date] NULL,
                                        )


                                        >



                                        declare @start datetime= getdate()
                                        declare @slamins int =960 --- SLA time in mins
                                        declare @Country varchar(2)='NA'
                                        declare @start_hour int = 8 -- business start hour
                                        declare @end_hour int = 17 -- business end hour
                                        declare @true bit = 'true'
                                        declare @false bit = 'false'
                                        declare @is_workday bit = @true
                                        declare @is_holiday bit = @false
                                        declare @due_date datetime
                                        declare @today_closing datetime
                                        declare @temp int = 0
                                        declare @holidays table (HDate DateTime) -- Table variable to hold holidayes

                                        ---- Get country holidays from table based on the country code (Feel free to remove this or modify as per your DB schema)
                                        Insert Into @Holidays (HDate) Select date from HOLIDAY Where region=@Country and Hdate>=DateAdd(dd, DateDiff(dd,0,@start), 0)

                                        --check for weekends
                                        set @start = case(datepart(dw,@start)+@@datefirst-1)%7
                                        when 0 then Convert(dateTime,CONVERT(varchar,@start+1,101)+' 08:00:00')
                                        when 6 then Convert(dateTime,CONVERT(varchar,@start+2,101)+' 08:00:00')
                                        else @start end

                                        -- check if start time is before business hour
                                        if datepart(hh, @start) < @start_hour set @start = Convert(dateTime,CONVERT(varchar,@start,101)+' 08:00:00')

                                        -- check if start time is after business hour
                                        if datepart(hh, @start) >= @end_hour set @start = Convert(dateTime,CONVERT(varchar,@start+1,101)+' 08:00:00')

                                        -- loop start
                                        while (@slamins > 0)
                                        begin
                                        -- prepared closing date time based on start date
                                        set @today_closing = Convert(dateTime,CONVERT(varchar,@start,101)+' 17:00:00')
                                        set @due_date = @start
                                        -- calculate number of Minute between start date and closing date
                                        set @temp = DATEDIFF(N, @start , @today_closing);

                                        --check for weekends
                                        if (DATEPART(dw, @start)!=1 AND DATEPART(dw, @start)!=7)
                                        set @is_workday = @true
                                        else
                                        set @is_workday = @false
                                        --check for holidays
                                        if (Select Count(*) From @Holidays Where HDATE=DateAdd(dd, DateDiff(dd,0,@start), 0)) = 0
                                        set @is_holiday =@false
                                        else
                                        set @is_holiday = @true
                                        if @is_workday = @true and @is_holiday = @false
                                        begin

                                        if(@temp < @slamins)
                                        begin
                                        set @slamins = @slamins - @temp
                                        end
                                        else
                                        begin
                                        set @due_date = DATEADD(MINUTE,@slamins,@start)
                                        set @slamins = 0
                                        print @due_date
                                        end
                                        end
                                        set @start = Convert(dateTime,CONVERT(varchar,@start+1,101)+' 08:00:00')
                                        end

                                        select @due_date






                                        share|improve this answer












                                        share|improve this answer



                                        share|improve this answer










                                        answered Jun 24 '16 at 11:26









                                        Subhash KadSubhash Kad

                                        111 bronze badge




                                        111 bronze badge





















                                            0














                                            I created a function to calculate due date from the table, once it's populated as per Beth's and others' approaches (various similar methods for doing this, as you can see -- it only took me about an hour to think about all the UK holidays and populate the table including Easter dates up to 2029 without using these exact guides).



                                            Note that my table contains SLA in business hours (8 hours in a normal day, 5 days in a normal week), your business hours may vary but you can amend this easily, just make sure your business hours are set the same for both the SLA table and the function below.



                                            Code below is T-SQL (written in SSMS v17.8.1)



                                            CREATE FUNCTION [JIRA].[Fn_JIRA_Due_Date] (
                                            @CreatedDate DATETIME, @SLA_Business_Hours INTEGER
                                            ) RETURNS DATETIME
                                            AS
                                            -- SELECT [JIRA].[Fn_JIRA_Due_Date]('2019-12-28 08:00:00', 24)

                                            /*

                                            baldmosher™
                                            2019-03-25

                                            * Function returns the DueDate for a JIRA ticket, based on the CreatedDate and the SLA (based on the Issue Type, or the Epic for Tasks) and business hours per date (set in [JIRA].[Ref_JIRA_Business_Hours])
                                            * Called by IUP to store this at the time the ticket is loaded
                                            * Can only consider SLA in Business Hours:
                                            * <24hrs calendar = <8hrs business
                                            * =24hrs calendar = 8hrs business
                                            * >24hrs calendar = 8hrs business * business days

                                            */


                                            BEGIN

                                            IF @CreatedDate IS NULL OR @SLA_Business_Hours IS NULL RETURN NULL;

                                            DECLARE @SLA_Hours_Remaining SMALLINT = @SLA_Business_Hours;

                                            --SET DATEFIRST 1;
                                            DECLARE @DueDate DATETIME;
                                            DECLARE @BusHrsStart DECIMAL(18,10) = 8 ; -- start of Business Hours (8am)
                                            DECLARE @BusHrsClose DECIMAL(18,10) = 16 ; -- close of Business Hours (4pm)
                                            --DECLARE @WkndStart DECIMAL(18,10) = 6 ; -- start of weekend (Sat)
                                            DECLARE @Hours_Today SMALLINT ; -- # hours left in day to process ticket

                                            -- PRINT 'Created ' + CAST(CAST(@CreatedDate AS DATE) AS VARCHAR(10)) + ' ' + CAST(CAST(@CreatedDate AS TIME) AS VARCHAR(8))

                                            --!!!! extend to the next whole hour just to simplify reporting -- need to work on fixing this eventually
                                            SET @CreatedDate = DATEADD(MINUTE,60-DATEPART(MINUTE,@CreatedDate),@CreatedDate)
                                            -- PRINT 'Rounded ' + CAST(CAST(@CreatedDate AS DATE) AS VARCHAR(10)) + ' ' + CAST(CAST(@CreatedDate AS TIME) AS VARCHAR(8))


                                            --check if created outside business hours and adjust CreatedDate to start the clock first thing at the next business hours start of day (days are checked next)
                                            IF DATEPART(HOUR,@CreatedDate) < @BusHrsStart
                                            --created before normal hours, adjust @CreatedDate later to @BusHrsStart same day
                                            BEGIN
                                            SET @CreatedDate = DATEADD(HOUR,@BusHrsStart,CAST(CAST(@CreatedDate AS DATE) AS DATETIME))
                                            END

                                            IF DATEPART(HOUR,@CreatedDate) >= @BusHrsClose
                                            --created after normal hours, adjust @CreatedDate to @BusHrsStart next day
                                            BEGIN
                                            SET @CreatedDate = DATEADD(HOUR,@BusHrsStart,CAST(CAST(@CreatedDate+1 AS DATE) AS DATETIME))
                                            --adjust CreatedDate to start the clock the next day with >0 business hours (i.e. extend if it falls on a weekend or holiday)
                                            SET @CreatedDate = CAST(@CreatedDate AS DATE)
                                            StartNextWorkingDay:
                                            IF (SELECT TOP(1) [Business_Hours] FROM [JIRA].[Ref_JIRA_Business_Hours] b WHERE b.[Date] = @CreatedDate ORDER BY [Date]) = 0
                                            BEGIN
                                            SET @CreatedDate = DATEADD(DAY,1,@CreatedDate)
                                            GOTO StartNextWorkingDay
                                            END
                                            --DATEADD(DAY, DATEDIFF(DAY,0,@CreatedDate+7)/7*7,0); -- midnight, Monday next week
                                            SET @CreatedDate = DATEADD(HOUR,@BusHrsStart,@CreatedDate); -- BusHrsStart
                                            END
                                            -- PRINT 'Started ' + CAST(CAST(@CreatedDate AS DATE) AS VARCHAR(10)) + ' ' + CAST(CAST(@CreatedDate AS TIME) AS VARCHAR(8))

                                            --third, check the business hours for each date from CreatedDate onwards to determine the relevant DueDate
                                            SET @DueDate = @CreatedDate
                                            -- PRINT 'SLA Hrs ' + CAST(@SLA_Hours_Remaining AS VARCHAR(2))
                                            SET @Hours_Today = @BusHrsStart + (SELECT TOP(1) [Business_Hours] FROM [JIRA].[Ref_JIRA_Business_Hours] b WHERE b.[Date] = CAST(@DueDate AS DATE) ORDER BY [Date]) - DATEPART(HOUR, @CreatedDate)
                                            -- PRINT 'Hrs Today ' + CAST(@Hours_Today AS VARCHAR(2))
                                            DueNextWorkingDay:
                                            IF @SLA_Hours_Remaining > @Hours_Today
                                            BEGIN
                                            -- PRINT 'Due another day'
                                            SET @SLA_Hours_Remaining = @SLA_Hours_Remaining - @Hours_Today --adjust remaining time after today's hours
                                            SET @Hours_Today = (SELECT TOP(1) [Business_Hours] FROM [JIRA].[Ref_JIRA_Business_Hours] b WHERE b.[Date] = CAST(@DueDate AS DATE) ORDER BY [Date])
                                            -- PRINT 'SLA Hrs ' + CAST(@SLA_Hours_Remaining AS VARCHAR(2))
                                            -- PRINT 'Hrs Today ' + CAST(@Hours_Today AS VARCHAR(2))
                                            SET @DueDate = DATEADD(DAY,1,DATEADD(HOUR,@BusHrsStart,CAST(CAST(@DueDate AS DATE) AS DATETIME))) --adjust DueDate to first thing next day
                                            END
                                            IF @SLA_Hours_Remaining <= @Hours_Today
                                            BEGIN
                                            -- PRINT 'Due today'
                                            SET @DueDate = DATEADD(HOUR,@SLA_Hours_Remaining,@DueDate)
                                            END
                                            ELSE
                                            BEGIN
                                            GOTO DueNextWorkingDay
                                            END

                                            -- PRINT 'DueDate ' + CAST(CAST(@DueDate AS DATE) AS VARCHAR(10)) + ' ' + CAST(CAST(@DueDate AS TIME) AS VARCHAR(8))

                                            RETURN @DueDate

                                            END

                                            GO


                                            Table for SLAs:



                                            CREATE TABLE [JIRA].[Ref_JIRA_SLAs](
                                            [SLA_SK] [SMALLINT] IDENTITY(1,1) NOT NULL,
                                            [Project] [VARCHAR](20) NULL,
                                            [Issue_Type] [VARCHAR](50) NULL,
                                            [Epic_Name] [VARCHAR](50) NULL,
                                            [SLA_Business_Hours] [SMALLINT] NULL,
                                            [Comments] [VARCHAR](8000) NULL
                                            ) ON [PRIMARY] WITH (DATA_COMPRESSION = PAGE)
                                            GO





                                            share|improve this answer





























                                              0














                                              I created a function to calculate due date from the table, once it's populated as per Beth's and others' approaches (various similar methods for doing this, as you can see -- it only took me about an hour to think about all the UK holidays and populate the table including Easter dates up to 2029 without using these exact guides).



                                              Note that my table contains SLA in business hours (8 hours in a normal day, 5 days in a normal week), your business hours may vary but you can amend this easily, just make sure your business hours are set the same for both the SLA table and the function below.



                                              Code below is T-SQL (written in SSMS v17.8.1)



                                              CREATE FUNCTION [JIRA].[Fn_JIRA_Due_Date] (
                                              @CreatedDate DATETIME, @SLA_Business_Hours INTEGER
                                              ) RETURNS DATETIME
                                              AS
                                              -- SELECT [JIRA].[Fn_JIRA_Due_Date]('2019-12-28 08:00:00', 24)

                                              /*

                                              baldmosher™
                                              2019-03-25

                                              * Function returns the DueDate for a JIRA ticket, based on the CreatedDate and the SLA (based on the Issue Type, or the Epic for Tasks) and business hours per date (set in [JIRA].[Ref_JIRA_Business_Hours])
                                              * Called by IUP to store this at the time the ticket is loaded
                                              * Can only consider SLA in Business Hours:
                                              * <24hrs calendar = <8hrs business
                                              * =24hrs calendar = 8hrs business
                                              * >24hrs calendar = 8hrs business * business days

                                              */


                                              BEGIN

                                              IF @CreatedDate IS NULL OR @SLA_Business_Hours IS NULL RETURN NULL;

                                              DECLARE @SLA_Hours_Remaining SMALLINT = @SLA_Business_Hours;

                                              --SET DATEFIRST 1;
                                              DECLARE @DueDate DATETIME;
                                              DECLARE @BusHrsStart DECIMAL(18,10) = 8 ; -- start of Business Hours (8am)
                                              DECLARE @BusHrsClose DECIMAL(18,10) = 16 ; -- close of Business Hours (4pm)
                                              --DECLARE @WkndStart DECIMAL(18,10) = 6 ; -- start of weekend (Sat)
                                              DECLARE @Hours_Today SMALLINT ; -- # hours left in day to process ticket

                                              -- PRINT 'Created ' + CAST(CAST(@CreatedDate AS DATE) AS VARCHAR(10)) + ' ' + CAST(CAST(@CreatedDate AS TIME) AS VARCHAR(8))

                                              --!!!! extend to the next whole hour just to simplify reporting -- need to work on fixing this eventually
                                              SET @CreatedDate = DATEADD(MINUTE,60-DATEPART(MINUTE,@CreatedDate),@CreatedDate)
                                              -- PRINT 'Rounded ' + CAST(CAST(@CreatedDate AS DATE) AS VARCHAR(10)) + ' ' + CAST(CAST(@CreatedDate AS TIME) AS VARCHAR(8))


                                              --check if created outside business hours and adjust CreatedDate to start the clock first thing at the next business hours start of day (days are checked next)
                                              IF DATEPART(HOUR,@CreatedDate) < @BusHrsStart
                                              --created before normal hours, adjust @CreatedDate later to @BusHrsStart same day
                                              BEGIN
                                              SET @CreatedDate = DATEADD(HOUR,@BusHrsStart,CAST(CAST(@CreatedDate AS DATE) AS DATETIME))
                                              END

                                              IF DATEPART(HOUR,@CreatedDate) >= @BusHrsClose
                                              --created after normal hours, adjust @CreatedDate to @BusHrsStart next day
                                              BEGIN
                                              SET @CreatedDate = DATEADD(HOUR,@BusHrsStart,CAST(CAST(@CreatedDate+1 AS DATE) AS DATETIME))
                                              --adjust CreatedDate to start the clock the next day with >0 business hours (i.e. extend if it falls on a weekend or holiday)
                                              SET @CreatedDate = CAST(@CreatedDate AS DATE)
                                              StartNextWorkingDay:
                                              IF (SELECT TOP(1) [Business_Hours] FROM [JIRA].[Ref_JIRA_Business_Hours] b WHERE b.[Date] = @CreatedDate ORDER BY [Date]) = 0
                                              BEGIN
                                              SET @CreatedDate = DATEADD(DAY,1,@CreatedDate)
                                              GOTO StartNextWorkingDay
                                              END
                                              --DATEADD(DAY, DATEDIFF(DAY,0,@CreatedDate+7)/7*7,0); -- midnight, Monday next week
                                              SET @CreatedDate = DATEADD(HOUR,@BusHrsStart,@CreatedDate); -- BusHrsStart
                                              END
                                              -- PRINT 'Started ' + CAST(CAST(@CreatedDate AS DATE) AS VARCHAR(10)) + ' ' + CAST(CAST(@CreatedDate AS TIME) AS VARCHAR(8))

                                              --third, check the business hours for each date from CreatedDate onwards to determine the relevant DueDate
                                              SET @DueDate = @CreatedDate
                                              -- PRINT 'SLA Hrs ' + CAST(@SLA_Hours_Remaining AS VARCHAR(2))
                                              SET @Hours_Today = @BusHrsStart + (SELECT TOP(1) [Business_Hours] FROM [JIRA].[Ref_JIRA_Business_Hours] b WHERE b.[Date] = CAST(@DueDate AS DATE) ORDER BY [Date]) - DATEPART(HOUR, @CreatedDate)
                                              -- PRINT 'Hrs Today ' + CAST(@Hours_Today AS VARCHAR(2))
                                              DueNextWorkingDay:
                                              IF @SLA_Hours_Remaining > @Hours_Today
                                              BEGIN
                                              -- PRINT 'Due another day'
                                              SET @SLA_Hours_Remaining = @SLA_Hours_Remaining - @Hours_Today --adjust remaining time after today's hours
                                              SET @Hours_Today = (SELECT TOP(1) [Business_Hours] FROM [JIRA].[Ref_JIRA_Business_Hours] b WHERE b.[Date] = CAST(@DueDate AS DATE) ORDER BY [Date])
                                              -- PRINT 'SLA Hrs ' + CAST(@SLA_Hours_Remaining AS VARCHAR(2))
                                              -- PRINT 'Hrs Today ' + CAST(@Hours_Today AS VARCHAR(2))
                                              SET @DueDate = DATEADD(DAY,1,DATEADD(HOUR,@BusHrsStart,CAST(CAST(@DueDate AS DATE) AS DATETIME))) --adjust DueDate to first thing next day
                                              END
                                              IF @SLA_Hours_Remaining <= @Hours_Today
                                              BEGIN
                                              -- PRINT 'Due today'
                                              SET @DueDate = DATEADD(HOUR,@SLA_Hours_Remaining,@DueDate)
                                              END
                                              ELSE
                                              BEGIN
                                              GOTO DueNextWorkingDay
                                              END

                                              -- PRINT 'DueDate ' + CAST(CAST(@DueDate AS DATE) AS VARCHAR(10)) + ' ' + CAST(CAST(@DueDate AS TIME) AS VARCHAR(8))

                                              RETURN @DueDate

                                              END

                                              GO


                                              Table for SLAs:



                                              CREATE TABLE [JIRA].[Ref_JIRA_SLAs](
                                              [SLA_SK] [SMALLINT] IDENTITY(1,1) NOT NULL,
                                              [Project] [VARCHAR](20) NULL,
                                              [Issue_Type] [VARCHAR](50) NULL,
                                              [Epic_Name] [VARCHAR](50) NULL,
                                              [SLA_Business_Hours] [SMALLINT] NULL,
                                              [Comments] [VARCHAR](8000) NULL
                                              ) ON [PRIMARY] WITH (DATA_COMPRESSION = PAGE)
                                              GO





                                              share|improve this answer



























                                                0












                                                0








                                                0







                                                I created a function to calculate due date from the table, once it's populated as per Beth's and others' approaches (various similar methods for doing this, as you can see -- it only took me about an hour to think about all the UK holidays and populate the table including Easter dates up to 2029 without using these exact guides).



                                                Note that my table contains SLA in business hours (8 hours in a normal day, 5 days in a normal week), your business hours may vary but you can amend this easily, just make sure your business hours are set the same for both the SLA table and the function below.



                                                Code below is T-SQL (written in SSMS v17.8.1)



                                                CREATE FUNCTION [JIRA].[Fn_JIRA_Due_Date] (
                                                @CreatedDate DATETIME, @SLA_Business_Hours INTEGER
                                                ) RETURNS DATETIME
                                                AS
                                                -- SELECT [JIRA].[Fn_JIRA_Due_Date]('2019-12-28 08:00:00', 24)

                                                /*

                                                baldmosher™
                                                2019-03-25

                                                * Function returns the DueDate for a JIRA ticket, based on the CreatedDate and the SLA (based on the Issue Type, or the Epic for Tasks) and business hours per date (set in [JIRA].[Ref_JIRA_Business_Hours])
                                                * Called by IUP to store this at the time the ticket is loaded
                                                * Can only consider SLA in Business Hours:
                                                * <24hrs calendar = <8hrs business
                                                * =24hrs calendar = 8hrs business
                                                * >24hrs calendar = 8hrs business * business days

                                                */


                                                BEGIN

                                                IF @CreatedDate IS NULL OR @SLA_Business_Hours IS NULL RETURN NULL;

                                                DECLARE @SLA_Hours_Remaining SMALLINT = @SLA_Business_Hours;

                                                --SET DATEFIRST 1;
                                                DECLARE @DueDate DATETIME;
                                                DECLARE @BusHrsStart DECIMAL(18,10) = 8 ; -- start of Business Hours (8am)
                                                DECLARE @BusHrsClose DECIMAL(18,10) = 16 ; -- close of Business Hours (4pm)
                                                --DECLARE @WkndStart DECIMAL(18,10) = 6 ; -- start of weekend (Sat)
                                                DECLARE @Hours_Today SMALLINT ; -- # hours left in day to process ticket

                                                -- PRINT 'Created ' + CAST(CAST(@CreatedDate AS DATE) AS VARCHAR(10)) + ' ' + CAST(CAST(@CreatedDate AS TIME) AS VARCHAR(8))

                                                --!!!! extend to the next whole hour just to simplify reporting -- need to work on fixing this eventually
                                                SET @CreatedDate = DATEADD(MINUTE,60-DATEPART(MINUTE,@CreatedDate),@CreatedDate)
                                                -- PRINT 'Rounded ' + CAST(CAST(@CreatedDate AS DATE) AS VARCHAR(10)) + ' ' + CAST(CAST(@CreatedDate AS TIME) AS VARCHAR(8))


                                                --check if created outside business hours and adjust CreatedDate to start the clock first thing at the next business hours start of day (days are checked next)
                                                IF DATEPART(HOUR,@CreatedDate) < @BusHrsStart
                                                --created before normal hours, adjust @CreatedDate later to @BusHrsStart same day
                                                BEGIN
                                                SET @CreatedDate = DATEADD(HOUR,@BusHrsStart,CAST(CAST(@CreatedDate AS DATE) AS DATETIME))
                                                END

                                                IF DATEPART(HOUR,@CreatedDate) >= @BusHrsClose
                                                --created after normal hours, adjust @CreatedDate to @BusHrsStart next day
                                                BEGIN
                                                SET @CreatedDate = DATEADD(HOUR,@BusHrsStart,CAST(CAST(@CreatedDate+1 AS DATE) AS DATETIME))
                                                --adjust CreatedDate to start the clock the next day with >0 business hours (i.e. extend if it falls on a weekend or holiday)
                                                SET @CreatedDate = CAST(@CreatedDate AS DATE)
                                                StartNextWorkingDay:
                                                IF (SELECT TOP(1) [Business_Hours] FROM [JIRA].[Ref_JIRA_Business_Hours] b WHERE b.[Date] = @CreatedDate ORDER BY [Date]) = 0
                                                BEGIN
                                                SET @CreatedDate = DATEADD(DAY,1,@CreatedDate)
                                                GOTO StartNextWorkingDay
                                                END
                                                --DATEADD(DAY, DATEDIFF(DAY,0,@CreatedDate+7)/7*7,0); -- midnight, Monday next week
                                                SET @CreatedDate = DATEADD(HOUR,@BusHrsStart,@CreatedDate); -- BusHrsStart
                                                END
                                                -- PRINT 'Started ' + CAST(CAST(@CreatedDate AS DATE) AS VARCHAR(10)) + ' ' + CAST(CAST(@CreatedDate AS TIME) AS VARCHAR(8))

                                                --third, check the business hours for each date from CreatedDate onwards to determine the relevant DueDate
                                                SET @DueDate = @CreatedDate
                                                -- PRINT 'SLA Hrs ' + CAST(@SLA_Hours_Remaining AS VARCHAR(2))
                                                SET @Hours_Today = @BusHrsStart + (SELECT TOP(1) [Business_Hours] FROM [JIRA].[Ref_JIRA_Business_Hours] b WHERE b.[Date] = CAST(@DueDate AS DATE) ORDER BY [Date]) - DATEPART(HOUR, @CreatedDate)
                                                -- PRINT 'Hrs Today ' + CAST(@Hours_Today AS VARCHAR(2))
                                                DueNextWorkingDay:
                                                IF @SLA_Hours_Remaining > @Hours_Today
                                                BEGIN
                                                -- PRINT 'Due another day'
                                                SET @SLA_Hours_Remaining = @SLA_Hours_Remaining - @Hours_Today --adjust remaining time after today's hours
                                                SET @Hours_Today = (SELECT TOP(1) [Business_Hours] FROM [JIRA].[Ref_JIRA_Business_Hours] b WHERE b.[Date] = CAST(@DueDate AS DATE) ORDER BY [Date])
                                                -- PRINT 'SLA Hrs ' + CAST(@SLA_Hours_Remaining AS VARCHAR(2))
                                                -- PRINT 'Hrs Today ' + CAST(@Hours_Today AS VARCHAR(2))
                                                SET @DueDate = DATEADD(DAY,1,DATEADD(HOUR,@BusHrsStart,CAST(CAST(@DueDate AS DATE) AS DATETIME))) --adjust DueDate to first thing next day
                                                END
                                                IF @SLA_Hours_Remaining <= @Hours_Today
                                                BEGIN
                                                -- PRINT 'Due today'
                                                SET @DueDate = DATEADD(HOUR,@SLA_Hours_Remaining,@DueDate)
                                                END
                                                ELSE
                                                BEGIN
                                                GOTO DueNextWorkingDay
                                                END

                                                -- PRINT 'DueDate ' + CAST(CAST(@DueDate AS DATE) AS VARCHAR(10)) + ' ' + CAST(CAST(@DueDate AS TIME) AS VARCHAR(8))

                                                RETURN @DueDate

                                                END

                                                GO


                                                Table for SLAs:



                                                CREATE TABLE [JIRA].[Ref_JIRA_SLAs](
                                                [SLA_SK] [SMALLINT] IDENTITY(1,1) NOT NULL,
                                                [Project] [VARCHAR](20) NULL,
                                                [Issue_Type] [VARCHAR](50) NULL,
                                                [Epic_Name] [VARCHAR](50) NULL,
                                                [SLA_Business_Hours] [SMALLINT] NULL,
                                                [Comments] [VARCHAR](8000) NULL
                                                ) ON [PRIMARY] WITH (DATA_COMPRESSION = PAGE)
                                                GO





                                                share|improve this answer















                                                I created a function to calculate due date from the table, once it's populated as per Beth's and others' approaches (various similar methods for doing this, as you can see -- it only took me about an hour to think about all the UK holidays and populate the table including Easter dates up to 2029 without using these exact guides).



                                                Note that my table contains SLA in business hours (8 hours in a normal day, 5 days in a normal week), your business hours may vary but you can amend this easily, just make sure your business hours are set the same for both the SLA table and the function below.



                                                Code below is T-SQL (written in SSMS v17.8.1)



                                                CREATE FUNCTION [JIRA].[Fn_JIRA_Due_Date] (
                                                @CreatedDate DATETIME, @SLA_Business_Hours INTEGER
                                                ) RETURNS DATETIME
                                                AS
                                                -- SELECT [JIRA].[Fn_JIRA_Due_Date]('2019-12-28 08:00:00', 24)

                                                /*

                                                baldmosher™
                                                2019-03-25

                                                * Function returns the DueDate for a JIRA ticket, based on the CreatedDate and the SLA (based on the Issue Type, or the Epic for Tasks) and business hours per date (set in [JIRA].[Ref_JIRA_Business_Hours])
                                                * Called by IUP to store this at the time the ticket is loaded
                                                * Can only consider SLA in Business Hours:
                                                * <24hrs calendar = <8hrs business
                                                * =24hrs calendar = 8hrs business
                                                * >24hrs calendar = 8hrs business * business days

                                                */


                                                BEGIN

                                                IF @CreatedDate IS NULL OR @SLA_Business_Hours IS NULL RETURN NULL;

                                                DECLARE @SLA_Hours_Remaining SMALLINT = @SLA_Business_Hours;

                                                --SET DATEFIRST 1;
                                                DECLARE @DueDate DATETIME;
                                                DECLARE @BusHrsStart DECIMAL(18,10) = 8 ; -- start of Business Hours (8am)
                                                DECLARE @BusHrsClose DECIMAL(18,10) = 16 ; -- close of Business Hours (4pm)
                                                --DECLARE @WkndStart DECIMAL(18,10) = 6 ; -- start of weekend (Sat)
                                                DECLARE @Hours_Today SMALLINT ; -- # hours left in day to process ticket

                                                -- PRINT 'Created ' + CAST(CAST(@CreatedDate AS DATE) AS VARCHAR(10)) + ' ' + CAST(CAST(@CreatedDate AS TIME) AS VARCHAR(8))

                                                --!!!! extend to the next whole hour just to simplify reporting -- need to work on fixing this eventually
                                                SET @CreatedDate = DATEADD(MINUTE,60-DATEPART(MINUTE,@CreatedDate),@CreatedDate)
                                                -- PRINT 'Rounded ' + CAST(CAST(@CreatedDate AS DATE) AS VARCHAR(10)) + ' ' + CAST(CAST(@CreatedDate AS TIME) AS VARCHAR(8))


                                                --check if created outside business hours and adjust CreatedDate to start the clock first thing at the next business hours start of day (days are checked next)
                                                IF DATEPART(HOUR,@CreatedDate) < @BusHrsStart
                                                --created before normal hours, adjust @CreatedDate later to @BusHrsStart same day
                                                BEGIN
                                                SET @CreatedDate = DATEADD(HOUR,@BusHrsStart,CAST(CAST(@CreatedDate AS DATE) AS DATETIME))
                                                END

                                                IF DATEPART(HOUR,@CreatedDate) >= @BusHrsClose
                                                --created after normal hours, adjust @CreatedDate to @BusHrsStart next day
                                                BEGIN
                                                SET @CreatedDate = DATEADD(HOUR,@BusHrsStart,CAST(CAST(@CreatedDate+1 AS DATE) AS DATETIME))
                                                --adjust CreatedDate to start the clock the next day with >0 business hours (i.e. extend if it falls on a weekend or holiday)
                                                SET @CreatedDate = CAST(@CreatedDate AS DATE)
                                                StartNextWorkingDay:
                                                IF (SELECT TOP(1) [Business_Hours] FROM [JIRA].[Ref_JIRA_Business_Hours] b WHERE b.[Date] = @CreatedDate ORDER BY [Date]) = 0
                                                BEGIN
                                                SET @CreatedDate = DATEADD(DAY,1,@CreatedDate)
                                                GOTO StartNextWorkingDay
                                                END
                                                --DATEADD(DAY, DATEDIFF(DAY,0,@CreatedDate+7)/7*7,0); -- midnight, Monday next week
                                                SET @CreatedDate = DATEADD(HOUR,@BusHrsStart,@CreatedDate); -- BusHrsStart
                                                END
                                                -- PRINT 'Started ' + CAST(CAST(@CreatedDate AS DATE) AS VARCHAR(10)) + ' ' + CAST(CAST(@CreatedDate AS TIME) AS VARCHAR(8))

                                                --third, check the business hours for each date from CreatedDate onwards to determine the relevant DueDate
                                                SET @DueDate = @CreatedDate
                                                -- PRINT 'SLA Hrs ' + CAST(@SLA_Hours_Remaining AS VARCHAR(2))
                                                SET @Hours_Today = @BusHrsStart + (SELECT TOP(1) [Business_Hours] FROM [JIRA].[Ref_JIRA_Business_Hours] b WHERE b.[Date] = CAST(@DueDate AS DATE) ORDER BY [Date]) - DATEPART(HOUR, @CreatedDate)
                                                -- PRINT 'Hrs Today ' + CAST(@Hours_Today AS VARCHAR(2))
                                                DueNextWorkingDay:
                                                IF @SLA_Hours_Remaining > @Hours_Today
                                                BEGIN
                                                -- PRINT 'Due another day'
                                                SET @SLA_Hours_Remaining = @SLA_Hours_Remaining - @Hours_Today --adjust remaining time after today's hours
                                                SET @Hours_Today = (SELECT TOP(1) [Business_Hours] FROM [JIRA].[Ref_JIRA_Business_Hours] b WHERE b.[Date] = CAST(@DueDate AS DATE) ORDER BY [Date])
                                                -- PRINT 'SLA Hrs ' + CAST(@SLA_Hours_Remaining AS VARCHAR(2))
                                                -- PRINT 'Hrs Today ' + CAST(@Hours_Today AS VARCHAR(2))
                                                SET @DueDate = DATEADD(DAY,1,DATEADD(HOUR,@BusHrsStart,CAST(CAST(@DueDate AS DATE) AS DATETIME))) --adjust DueDate to first thing next day
                                                END
                                                IF @SLA_Hours_Remaining <= @Hours_Today
                                                BEGIN
                                                -- PRINT 'Due today'
                                                SET @DueDate = DATEADD(HOUR,@SLA_Hours_Remaining,@DueDate)
                                                END
                                                ELSE
                                                BEGIN
                                                GOTO DueNextWorkingDay
                                                END

                                                -- PRINT 'DueDate ' + CAST(CAST(@DueDate AS DATE) AS VARCHAR(10)) + ' ' + CAST(CAST(@DueDate AS TIME) AS VARCHAR(8))

                                                RETURN @DueDate

                                                END

                                                GO


                                                Table for SLAs:



                                                CREATE TABLE [JIRA].[Ref_JIRA_SLAs](
                                                [SLA_SK] [SMALLINT] IDENTITY(1,1) NOT NULL,
                                                [Project] [VARCHAR](20) NULL,
                                                [Issue_Type] [VARCHAR](50) NULL,
                                                [Epic_Name] [VARCHAR](50) NULL,
                                                [SLA_Business_Hours] [SMALLINT] NULL,
                                                [Comments] [VARCHAR](8000) NULL
                                                ) ON [PRIMARY] WITH (DATA_COMPRESSION = PAGE)
                                                GO






                                                share|improve this answer














                                                share|improve this answer



                                                share|improve this answer








                                                edited Mar 29 at 18:55

























                                                answered Mar 25 at 12:28









                                                baldmosherbaldmosher

                                                898 bronze badges




                                                898 bronze badges



























                                                    draft saved

                                                    draft discarded
















































                                                    Thanks for contributing an answer to Stack Overflow!


                                                    • Please be sure to answer the question. Provide details and share your research!

                                                    But avoid


                                                    • Asking for help, clarification, or responding to other answers.

                                                    • Making statements based on opinion; back them up with references or personal experience.

                                                    To learn more, see our tips on writing great answers.




                                                    draft saved


                                                    draft discarded














                                                    StackExchange.ready(
                                                    function ()
                                                    StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f27682810%2fcalculating-due-date-using-business-hours-and-holidays%23new-answer', 'question_page');

                                                    );

                                                    Post as a guest















                                                    Required, but never shown





















































                                                    Required, but never shown














                                                    Required, but never shown












                                                    Required, but never shown







                                                    Required, but never shown

































                                                    Required, but never shown














                                                    Required, but never shown












                                                    Required, but never shown







                                                    Required, but never shown







                                                    Popular posts from this blog

                                                    Kamusi Yaliyomo Aina za kamusi | Muundo wa kamusi | Faida za kamusi | Dhima ya picha katika kamusi | Marejeo | Tazama pia | Viungo vya nje | UrambazajiKuhusu kamusiGo-SwahiliWiki-KamusiKamusi ya Kiswahili na Kiingerezakuihariri na kuongeza habari

                                                    Swift 4 - func physicsWorld not invoked on collision? The Next CEO of Stack OverflowHow to call Objective-C code from Swift#ifdef replacement in the Swift language@selector() in Swift?#pragma mark in Swift?Swift for loop: for index, element in array?dispatch_after - GCD in Swift?Swift Beta performance: sorting arraysSplit a String into an array in Swift?The use of Swift 3 @objc inference in Swift 4 mode is deprecated?How to optimize UITableViewCell, because my UITableView lags

                                                    Access current req object everywhere in Node.js ExpressWhy are global variables considered bad practice? (node.js)Using req & res across functionsHow do I get the path to the current script with Node.js?What is Node.js' Connect, Express and “middleware”?Node.js w/ express error handling in callbackHow to access the GET parameters after “?” in Express?Modify Node.js req object parametersAccess “app” variable inside of ExpressJS/ConnectJS middleware?Node.js Express app - request objectAngular Http Module considered middleware?Session variables in ExpressJSAdd properties to the req object in expressjs with Typescript