DROP FUNCTION dbo.BusinessHours
GO
CREATE FUNCTION dbo.BusinessHours
(
@StartDate datetime, –start of period of consideration for calculation of business hours
@EndDate datetime, –end of period of consideration for calculation of business hours
@BusinessStart datetime, –start of business hours
@BusinessEnd datetime –end of business hours
)
RETURNS int
AS
BEGIN
DECLARE @TotalHours int
IF DATEDIFF(dd,0,@StartDate)=DATEDIFF(dd,0,@EndDate)
BEGIN
IF @BusinessStart>@BusinessEnd
BEGIN
SET @TotalHours= -1
END
ELSE
BEGIN
SELECT @BusinessStart=CASE WHEN DATEADD(dd,-DATEDIFF(dd,0,@StartDate),@StartDate) > @BusinessStart AND DATEADD(dd,-DATEDIFF(dd,0,@StartDate),@StartDate) < @BusinessEnd
THEN DATEADD(dd,-DATEDIFF(dd,0,@StartDate),@StartDate) ELSE @BusinessStart END,
@BusinessEnd =CASE WHEN DATEADD(dd,-DATEDIFF(dd,0,@EndDate),@EndDate) > @BusinessStart AND DATEADD(dd,-DATEDIFF(dd,0,@EndDate),@EndDate) < @BusinessEnd
THEN DATEADD(dd,-DATEDIFF(dd,0,@EndDate),@EndDate) ELSE @BusinessEnd END
SET @TotalHours= CASE WHEN DATEADD(dd,-DATEDIFF(dd,0,@StartDate),@StartDate) > @BusinessEnd OR DATEADD(dd,-DATEDIFF(dd,0,@EndDate),@EndDate) < @BusinessStart
THEN 0
ELSE DATEDIFF(mi,@BusinessStart,@BusinessEnd)/60
END
END
END
ELSE
BEGIN
SELECT @StartDate= CASE WHEN DATEADD(dd,-DATEDIFF(dd,0,@StartDate),@StartDate) > @BusinessEnd THEN DATEADD(ss,DATEDIFF(ss,0,@BusinessStart),DATEADD(dd,DATEDIFF(dd,0,@StartDate),1))
WHEN DATEADD(dd,-DATEDIFF(dd,0,@StartDate),@StartDate) < @BusinessStart THEN DATEADD(ss,DATEDIFF(ss,0,@BusinessStart),DATEADD(dd,DATEDIFF(dd,0,@StartDate),0))
ELSE @StartDate
END,
@EndDate= CASE WHEN DATEADD(dd,-DATEDIFF(dd,0,@EndDate),@EndDate) < @BusinessStart THEN DATEADD(ss,DATEDIFF(ss,0,@BusinessEnd),DATEADD(dd,DATEDIFF(dd,0,@EndDate),-1))
WHEN DATEADD(dd,-DATEDIFF(dd,0,@EndDate),@EndDate) > @BusinessEnd THEN DATEADD(ss,DATEDIFF(ss,0,@BusinessEnd),DATEADD(dd,DATEDIFF(dd,0,@EndDate),0))
ELSE @EndDate
END
;With Calendar_CTE (Date,Day,WeekDay)
AS
(
SELECT DATEADD(dd,DATEDIFF(dd,0,@StartDate),0),DATENAME(dw,DATEADD(dd,DATEDIFF(dd,0,@StartDate),0)), CASE WHEN DATEDIFF(dd,0,DATEADD(dd,DATEDIFF(dd,0,@StartDate),0))%7 < 5 THEN 1 ELSE 0 END
UNION ALL
SELECT DATEADD(dd,1,Date),DATENAME(dw,DATEADD(dd,1,Date)), CASE WHEN DATEDIFF(dd,0,DATEADD(dd,1,Date))%7 < 5 THEN 1 ELSE 0 END
FROM Calendar_CTE
WHERE DATEADD(dd,1,Date) <= @EndDate
)
SELECT @TotalHours=CEILING(SUM(
CASE WHEN DATEADD(dd,DATEDIFF(dd,0,@StartDate),0)=DATEADD(dd,DATEDIFF(dd,0,@EndDate),0) THEN DATEDIFF(MINUTE,@StartDate,@EndDate)
WHEN c.Date=DATEADD(dd,DATEDIFF(dd,0,@StartDate),0) THEN DATEDIFF(MINUTE,DATEADD(dd,-DATEDIFF(dd,0,@StartDate),@StartDate),@BusinessEnd)
WHEN c.Date=DATEADD(dd,DATEDIFF(dd,0,@EndDate),0) THEN DATEDIFF(MINUTE,@BusinessStart,DATEADD(dd,-DATEDIFF(dd,0,@EndDate),@EndDate))
ELSE DATEDIFF(MINUTE,@BusinessStart,@BusinessEnd)
END
)/60.0)
FROM Calendar_CTE c
LEFT JOIN Holiday h
ON h.[date]= c.Date
WHERE WeekDay=1
AND h.[date] IS NULL
OPTION (MAXRECURSION 0)
END
RETURN @TotalHours
END
This is a very useful function however if the Startdate and Enddate are the same day it returns an incorrect value.
I've added that check also now. Thanks for the feedback 🙂
How can this be edit to diplay minutes rather than just the hours between
Drop the /60 part in last select which retrieves value for @TotalHours and it will give total in minutes
Thanks for this useful code! This has saved us a few hours of head scratching! I'm using this to calculate minutes, however, if the dates are the same, this returns 0 minutes. Thanks for your help
Can you show how you're passing the dates?
Thank you for sharing this function! I have found a small issue…when you run the following (same start and end date that have times outside of the business hours) it will return 9 instead of 0.
SELECT dbo.GetBusinessHours('05/03/2010 18:00', '05/03/2010 20:00', '08:00', '17:00'))