I need to calculate the number of \"active minutes\" for an event within a database. The start-time is well known.
The complication is that these active minutes sho
I started working with what Unreason posted and was a great start. I tested this is SQL Server and found not all time was being captured. I think the problem was primarily when the event started and ended the same day. This solution seems to be working well enough for me
CREATE TABLE [dbo].[working_hours](
[wh_id] [int] IDENTITY(1,1) NOT FOR REPLICATION NOT NULL,
[wh_starttime] [datetime] NULL,
[wh_endtime] [datetime] NULL,
PRIMARY KEY CLUSTERED
(
[wh_id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
GO
CREATE FUNCTION [dbo].[udFWorkingMinutes]
(
@startdate DATETIME
,@enddate DATETIME
)
RETURNS INT
AS
BEGIN
DECLARE @WorkingHours INT
SET @WorkingHours =
(SELECT
CASE WHEN COALESCE(SUM(duration),0) < 0 THEN 0 ELSE SUM(Duration)
END AS Minutes
FROM
(
--All whole days
SELECT ISNULL(DATEDIFF(mi,wh_starttime,wh_endtime),0) AS Duration
FROM working_hours
WHERE wh_starttime >= @startdate AND wh_endtime <= @enddate
UNION ALL
--All partial days where event start after office hours and finish after office hours
SELECT ISNULL(DATEDIFF(mi,@startdate,wh_endtime),0) AS Duration
FROM working_hours
WHERE @startdate > wh_starttime AND @enddate >= wh_endtime
AND (CAST(wh_starttime AS DATE) = CAST(@startdate AS DATE))
AND @startdate < wh_endtime
UNION ALL
--All partial days where event starts before office hours and ends before day end
SELECT ISNULL(DATEDIFF(mi,wh_starttime,@enddate),0) AS Duration
FROM working_hours
WHERE @enddate < wh_endtime
AND @enddate >= wh_starttime
AND @startdate <= wh_starttime
AND (CAST(wh_endtime AS DATE) = CAST(@enddate AS DATE))
UNION ALL
--Get partial day where intraday event
SELECT ISNULL(DATEDIFF(mi,@startdate,@enddate),0) AS Duration
FROM working_hours
WHERE @startdate > wh_starttime AND @enddate < wh_endtime
AND (CAST(@startdate AS DATE)= CAST(wh_starttime AS DATE))
AND (CAST(@enddate AS DATE)= CAST(wh_endtime AS DATE))
) AS u)
RETURN @WorkingHours
END
GO
Alls that is left to do is populate the working hours table with something like
;WITH cte AS (
SELECT CASE WHEN DATEPART(Day,'2014-01-01 9:00:00 AM') = 1 THEN '2014-01-01 9:00:00 AM'
ELSE DATEADD(Day,DATEDIFF(Day,0,'2014-01-01 9:00:00 AM')+1,0) END AS myStartDate,
CASE WHEN DATEPART(Day,'2014-01-01 5:00:00 PM') = 1 THEN '2014-01-01 5:00:00 PM'
ELSE DATEADD(Day,DATEDIFF(Day,0,'2014-01-01 5:00:00 PM')+1,0) END AS myEndDate
UNION ALL
SELECT DATEADD(Day,1,myStartDate), DATEADD(Day,1,myEndDate)
FROM cte
WHERE DATEADD(Day,1,myStartDate) <= '2015-01-01'
)
INSERT INTO working_hours
SELECT myStartDate, myEndDate
FROM cte
OPTION (MAXRECURSION 0)
delete from working_hours where datename(dw,wh_starttime) IN ('Saturday', 'Sunday')
--delete public holidays
delete from working_hours where CAST(wh_starttime AS DATE) = '2014-01-01'
My first post! Be merciful.