Calculate time difference (only working hours) in minutes between two dates

前端 未结 5 1134
长情又很酷
长情又很酷 2020-12-06 03:30

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

5条回答
  •  情话喂你
    2020-12-06 04:07

    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.

提交回复
热议问题