33

I am using this query to get time taken.

SELECT DATEDIFF(dd, ActualStartDate, ActualCompletionDate) AS TimeTaken
FROM TableName

Now I want to exclude weekends and only include Mon-Fri as days counted.

Jakub Konecki
  • 45,581
  • 7
  • 87
  • 126
Sami
  • 3,956
  • 9
  • 37
  • 52
  • 2
    possible duplicate of [Count work days between two dates in T-SQL](http://stackoverflow.com/questions/252519/count-work-days-between-two-dates-in-t-sql) – Mikael Eriksson Sep 12 '11 at 13:31
  • Possible duplicate of [Count work days between two dates](http://stackoverflow.com/questions/252519/count-work-days-between-two-dates) – splattne Mar 06 '17 at 07:12
  • Skip the answers below, they're mostly incorrect. Use the links above instead. – John Jun 05 '19 at 16:34

10 Answers10

64

Example query below, here are some details on how I solved it.

Using DATEDIFF(WK, ...) will give us the number of weeks between the 2 dates. SQL Server evaluates this as a difference between week numbers rather than based on the number of days. This is perfect, since we can use this to determine how many weekends passed between the dates.

So we can multiple that value by 2 to get the number of weekend days that occurred and subtract that from the DATEDIFF(dd, ...) to get the number of weekdays.

This doesn't behave 100% correctly when the start or end date falls on Sunday, though. So I added in some case logic at the end of the calculation to handle those instances.

You may also want to consider whether or not the DATEDIFF should be fully inclusive. e.g. Is the difference between 9/10 and 9/11 1 day or 2 days? If the latter, you'll want to add 1 to the final product.

declare @d1 datetime, @d2 datetime
select @d1 = '9/9/2011',  @d2 = '9/18/2011'

select datediff(dd, @d1, @d2) - (datediff(wk, @d1, @d2) * 2) -
       case when datepart(dw, @d1) = 1 then 1 else 0 end +
      case when datepart(dw, @d2) = 1 then 1 else 0 end
Shankar R10N
  • 4,926
  • 1
  • 21
  • 24
Derek
  • 21,828
  • 7
  • 53
  • 61
  • 9
    Be wary of localisation issues with the two adjustments at the end as in the UK the Sunday check is interpreted as checking for Monday (my week starts on Monday, day = 1, looks like yours starts on Sunday as day 1). – Ryan O'Neill Apr 19 '12 at 14:47
  • 2
    Can someone explain this a little more? I would like to understand it in order to modify it to fit my needs. I thought I understood it cause if I use d1 = 1/3/2017 and d2 = 1/8/2017017 returns 4 which makes sense if it's inclusive but if I use d1=1/4/2017 and d2 = 1/12/2017 it returns 6 shouldn't it be 7? I clearly don't – Jcorretjer Feb 26 '17 at 00:47
  • @Jcorretjer, this answer is more for midnight to midnight, but then does behave strangely over a weekend (for example try two consecutive weekdays). So, it's actually the 1/3 to 1/8 that's incorrect if anything. Try experimenting with a Friday to a Saturday, then to Sunday, then to Monday. They're all 1. I can't see an application where this query in it's current form would be very useful. Notice the difference between this answer and the 'possible duplicate' linked answer above the question by Mikael Eriksson, which is probably more what you were looking for. – John Jun 04 '19 at 19:04
  • it will not work when start date = '2021-03-04' and end date = '2021-03-06' This function does not exclude weekends in this case. – Sujit Patel Mar 12 '21 at 05:16
  • This is a somewhat optimistic/unreliable answer due to week dates depending on first day of the week setting – siggemannen Feb 25 '23 at 13:37
6

I found when i used this there was a problem when d1 fell on saturday. Below is what i used to correct this.

declare @d1 datetime, @d2 datetime
select @d1 = '11/19/2011' ,  @d2 = '11/28/2011'

select datediff(dd, @d1, @d2) +case when datepart(dw, @d1) = 7 then 1 else 0 end - (datediff(wk, @d1, @d2) * 2) -
 case when datepart(dw, @d1) = 1 then 1 else 0 end +
 case when datepart(dw, @d2) = 1 then 1 else 0 end
dimcookies
  • 1,930
  • 7
  • 31
  • 37
JeffFischer30
  • 61
  • 1
  • 1
  • 2
    the answer by Derek is correct and there is no need to add a case for days starting from Saturday. – Blazen Apr 07 '15 at 18:00
2
BEGIN 
DECLARE @totaldays INT; 
DECLARE @weekenddays INT;

SET @totaldays = DATEDIFF(DAY, @startDate, @endDate) 
SET @weekenddays = ((DATEDIFF(WEEK, @startDate, @endDate) * 2) + -- get the number of weekend days in between
                       CASE WHEN DATEPART(WEEKDAY, @startDate) = 1 THEN 1 ELSE 0 END + -- if selection was Sunday, won't add to weekends
                       CASE WHEN DATEPART(WEEKDAY, @endDate) = 6 THEN 1 ELSE 0 END)  -- if selection was Saturday, won't add to weekends

Return (@totaldays - @weekenddays)

END

This is on SQL Server 2014

JBelfort
  • 113
  • 4
1
declare @d1 datetime, @d2 datetime
select @d1 = '4/19/2017',  @d2 = '5/7/2017'

DECLARE @Counter int = datediff(DAY,@d1 ,@d2 )

DECLARE @C int = 0
DECLARE @SUM int = 0





 WHILE  @Counter > 0
  begin
 SET @SUM = @SUM + IIF(DATENAME(dw, 

 DATEADD(day,@c,@d1))IN('Sunday','Monday','Tuesday','Wednesday','Thursday')
 ,1,0)



SET @Counter = @Counter - 1
set @c = @c +1
end

select @Sum
1

If you hate CASE statements as much as I do, and want to be able to use the solution inline in your queries, just get the difference of days and subtract the count of weekend days and you'll have the desired result:

declare @d1 datetime, @d2 datetime,  @days int
select @d1 = '2018/10/01',  @d2 = '2018/11/01'

SET @days = DateDiff(dd, @d1, @d2) - DateDiff(ww, @d1, @d2)*2
print @days

(The only caveat--or at least point to keep in mind--is that this calculation is not inclusive of the last date, so you might need to add one day to the end date to achieve an inclusive result)

Frank Bailey
  • 185
  • 2
  • 17
0

I just want to share the code I created that might help you.

DECLARE @MyCounter int = 0, @TempDate datetime, @EndDate datetime;
SET @TempDate = DATEADD(d,1,'2017-5-27')
SET @EndDate = '2017-6-3'

WHILE @TempDate <= @EndDate
    BEGIN
    IF DATENAME(DW,@TempDate) = 'Sunday' OR DATENAME(DW,@TempDate) = 'Saturday'
        SET @MyCounter = @MyCounter
    ELSE IF @TempDate not in ('2017-1-1', '2017-1-16', '2017-2-20', '2017-5-29', '2017-7-4', '2017-9-4', '2017-10-9', '2017-11-11', '2017-12-25')
        SET @MyCounter = @MyCounter + 1

    SET @TempDate = DATEADD(d,1,@TempDate)
    CONTINUE

END
PRINT @MyCounter
PRINT @TempDate

If you do have a holiday table, you can also use that so that you don't have to list all the holidays in the ELSE IF section of the code. You can also create a function for this code and use the function whenever you need it in your query.

I hope this might help too.

0

Using https://stackoverflow.com/a/1804095 and JeffFisher30's answer above (https://stackoverflow.com/a/14572370/6147425) and my own need to have fractional days, I wrote this:

DateDiff(second,Start_Time,End_Time)/86400.0
            -2*DateDiff(week, Start_Time, End_Time)
            -Case When (DatePart(weekday, Start_Time)+@@DateFirst)%7 = 1 Then 1 Else 0 End
            +Case When (DatePart(weekday, End_Time)+@@DateFirst)%7 = 1 Then 1 Else 0 End
Jeff Winchell
  • 103
  • 1
  • 8
0

Use this function to calculate the number of business days excluding Saturday and Sunday. Also it will exclude start date and it will include end date.

-- Select [dbo].[GetBussinessDays]  ('02/18/2021', '03/06/2021') -- 11 days
CREATE or ALTER FUNCTION [dbo].[GetBussinessDays] ( 
    @StartDate DATETIME,
    @EndDate  DATETIME 
)
returns INT AS
BEGIN
  DECLARE @tempStartDate     DATETIME= @StartDate;
  DECLARE @tempEndDate       DATETIME = @EndDate;
  
  IF(@tempStartDate IS NULL
  OR
  @tempEndDate IS NULL)
  BEGIN
    RETURN NULL;
  END
  --To avoid negative values reverse the date if StartDate is grater than EndDate
  IF(@StartDate > @EndDate)
  BEGIN
    SET @StartDate = @tempEndDate;
    SET @EndDate = @tempStartDate;
  END

  DECLARE @Counter           INT = Datediff(day,@StartDate ,@EndDate);
  DECLARE @TempCounter       INT = 0;
  DECLARE @TotalBusinessDays INT = 0;

  WHILE @Counter >= 0
  BEGIN
    IF(@TempCounter > 0 OR @Counter = 1) -- To ignore first day's calculation
    Begin
        SET @TotalBusinessDays = @TotalBusinessDays + Iif(Datename(dw, Dateadd(day,@TempCounter,@StartDate)) IN('Monday',
                                                                                                         'Tuesday',
                                                                                                         'Wednesday',
                                                                                                         'Thursday',
                                                                                                         'Friday'),1,0)
    END

    SET @Counter = @Counter - 1
    SET @TempCounter = @TempCounter +1
  END
  RETURN @TotalBusinessDays;
END
Santhosh N
  • 62
  • 4
0

Using @Derek Kromm answer (Current Marked Answer)

I have modified so it IS tolerant of any localisations that may be on the target server.

DECLARE @d1 DATETIME, @d2 DATETIME
SELECT @d1 = '10/01/2022',  @d2 = '10/28/2022'

SELECT (datediff(dd, @d1, @EndQuery)+1) - (datediff(wk, @d1, dateadd(dd,1,@d2)) * 2) 
  - CASE WHEN DateName(WEEKDAY, @d1) = 'Sunday' THEN 1 ELSE 0 END -- This includes for start date edge case
  + CASE WHEN DateName(WEEKDAY, @d2) = 'Saturday'  THEN 1 ELSE 0 END -- This includes for  end date edge case.

This is with the end date being innclusive.

adamrob
  • 9
  • 3
-1

/* EXAMPLE: /MONDAY/ SET DATEFIRST 1 SELECT dbo.FUNC_GETDATEDIFFERENCE_WO_WEEKEND('2019-02-01','2019-02-12') */ CREATE FUNCTION FUNC_GETDATEDIFFERENCE_WO_WEEKEND ( @pdtmaLastLoanPayDate DATETIME, @pdtmaDisbursedDate DATETIME ) RETURNS BIGINT BEGIN

DECLARE
@mintDaysDifference     BIGINT

SET @mintDaysDifference = 0

WHILE CONVERT(NCHAR(10),@pdtmaLastLoanPayDate,121) <= CONVERT(NCHAR(10),@pdtmaDisbursedDate,121)
BEGIN
    IF DATEPART(WEEKDAY,@pdtmaLastLoanPayDate) NOT IN (6,7)
    BEGIN
        SET @mintDaysDifference = @mintDaysDifference + 1
    END

    SET @pdtmaLastLoanPayDate = DATEADD(DAY,1,@pdtmaLastLoanPayDate)
END

RETURN ISNULL(@mintDaysDifference,0)

END

yanze
  • 1