Mysql : how to calculate business hrs between two timestamps and neglect weekends

后端 未结 4 1679
南笙
南笙 2021-02-04 06:38

My table has sample data and I need to calculate business hrs between two timestamps in two columns. Business hrs : 9:00am to 5:00pm and neglect Saturday and Sunday, I am not co

相关标签:
4条回答
  • 2021-02-04 07:11

    The question says that public holidays should not be considered, so this answer does just that - calculates business hours taking weekends into account, but ignoring possible public holidays.

    If you need to take public holidays into account you'd need to have a separate table which would list dates for public holidays, which may differ from year to year and from state to state or country to country. The main formula may stay the same, but you'd need to subtract from its result hours for public holidays that fall within the given range of dates.

    Let's create a table with some sample data that covers various cases:

    CREATE TABLE T (CreatedDate datetime, UpdatedDate datetime);
    
    INSERT INTO T VALUES
    ('2012-03-05 09:00:00', '2012-03-05 15:00:00'), -- simple part of the same day
    ('2012-03-05 10:00:00', '2012-03-06 10:00:00'), -- full day across the midnight
    ('2012-03-05 11:00:00', '2012-03-06 10:00:00'), -- less than a day across the midnight
    ('2012-03-05 10:00:00', '2012-03-06 15:00:00'), -- more than a day across the midnight
    ('2012-03-09 16:00:00', '2012-03-12 10:00:00'), -- over the weekend, less than 7 days
    ('2012-03-06 16:00:00', '2012-03-15 10:00:00'), -- over the weekend, more than 7 days
    ('2012-03-09 16:00:00', '2012-03-19 10:00:00'); -- over two weekends
    

    In MS SQL Server I use the following formula:

    SELECT
        CreatedDate,
        UpdatedDate,
        DATEDIFF(minute, CreatedDate, UpdatedDate)/60.0 -
        DATEDIFF(day,    CreatedDate, UpdatedDate)*16 -
        DATEDIFF(week,   CreatedDate, UpdatedDate)*16 AS BusinessHours
    FROM T
    

    Which produces the following result:

    +-------------------------+-------------------------+---------------+
    |       CreatedDate       |       UpdatedDate       | BusinessHours |
    +-------------------------+-------------------------+---------------+
    | 2012-03-05 09:00:00     | 2012-03-05 15:00:00     | 6             |
    | 2012-03-05 10:00:00     | 2012-03-06 10:00:00     | 8             |
    | 2012-03-05 11:00:00     | 2012-03-06 10:00:00     | 7             |
    | 2012-03-05 10:00:00     | 2012-03-06 15:00:00     | 13            |
    | 2012-03-09 16:00:00     | 2012-03-12 10:00:00     | 2             |
    | 2012-03-06 16:00:00     | 2012-03-15 10:00:00     | 50            |
    | 2012-03-09 16:00:00     | 2012-03-19 10:00:00     | 42            |
    +-------------------------+-------------------------+---------------+
    

    It works, because in SQL Server DATEDIFF returns the count of the specified datepart boundaries crossed between the specified startdate and enddate.

    Each day has 8 business hours. I calculate total number of hours between two dates, then subtract the number of midnights multiplied by 16 non-business hours per day, then subtract the number of weekends multiplied by 16 (8+8 business hours for Sat+Sun).

    It also assumes that the given start and end date/times are during the business hours.

    In MySQL the closest equivalent is TIMESTAMPDIFF, but it works differently. It effectively calculates the difference in seconds and divides (discarding the fractional part) by the number of seconds in the chosen unit.

    So, to get results that we need we can calculate the TIMESTAMPDIFF between some anchor datetime and CreatedDate and UpdatedDate instead of calculating the difference between CreatedDate and UpdatedDate directly.

    I've chosen 2000-01-03 00:00:00, which is a Monday. You can choose any other Monday (or Sunday, if your week starts on Sunday) midnight for the anchor date.

    The MySQL query becomes (see SQL Fiddle):

    SELECT
        CreatedDate,
        UpdatedDate,
    
        TIMESTAMPDIFF(MINUTE, CreatedDate, UpdatedDate)/60.0 -
    
        16*(
            TIMESTAMPDIFF(DAY,    '2000-01-03',UpdatedDate)-
            TIMESTAMPDIFF(DAY,    '2000-01-03',CreatedDate)
        ) -
    
        16*(
            TIMESTAMPDIFF(WEEK,   '2000-01-03',UpdatedDate)-
            TIMESTAMPDIFF(WEEK,   '2000-01-03',CreatedDate)
        ) AS BusinessHours
    
    FROM T
    
    0 讨论(0)
  • 2021-02-04 07:14

    try

    SELECT (FLOOR(DATEDIFF (UpdatedDate, CreatedDate) / 7) * 5 + MOD (DATEDIFF (UpdatedDate, CreatedDate), 7)) * 8 +
           TIMEDIFF (TIME (UpdatedDate), TIME(CreatedDate))
    FROM YourTable
    

    The above is not complete as it only "estimates" the weekends... but it should get you started... another option is to work with a table of valid businesshours (for details see this answer from Laurence).

    For reference see MySQL documentation.

    0 讨论(0)
  • 2021-02-04 07:16

    Use datediff to calculate the diff between the start and the end time and then subtract (date_add with negative hours) the non-working hours: 24 for weekend days, 16 for working days expect the ending and starting day which needs additional calculation. Whether a day is weekend day or not, can be specified with the dayofweek method.

    0 讨论(0)
  • 2021-02-04 07:28

    Create a table called BusinessHours, with a single column called Hour containing a date time. Make Hour the primary key. Write a process to populate it with every business hour of the year (2012-01-02 00:90:00, 2012-01-02 00:10:00 etc). This shouldn't be hard as the rules are straightforward. It sounds like a lot of data, but in the grand scheme of things it isn't (there are only 8760 hours in a year - even if your date time takes 8 bytes each, this is a whopping 60kB). Make sure to schedule a job to keep it populated.

    Then:

    Select
      y.CreatedDate,
      y.UpdatedDate,
      Count(*) as BusinessHours
    From
      YourTable y
        Inner Join
      BusinessHours h
        On h.Hour >= y.CreatedDate And h.Hour < y.UpdatedDate
    Group By
      y.CreatedDate,
      y.UpdatedDate
    

    This also gives you a fairly straightforward approach if you do ever consider public holidays - just take the rows out of the BusinessHours table.

    0 讨论(0)
提交回复
热议问题