Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to extract Business Hours between two different times values

Tags:

datetime

time

r

For the evaluation of tickets processed by a help desk I would like to know how many business hours an ticket is active. I can easily subtract the times and get the total amount of hours. But the only hours that should count are between 08:30 and 18:00.

For example: if a ticket is created at 11/23/2015 10:20 and completed on 11/24/2015 17:20, then 31 'normal' hours have passed. I'm only interested in the business hours (between 8:30 and 18:00) that have passed; in this case 16 hours and 30 minutes

like image 587
M.Vennemans Avatar asked Jan 18 '26 22:01

M.Vennemans


1 Answers

I have tried the solutions of both @mvan and @Steffan Jansson. Unfortunately they both didn't suit my needs. The first returns incorrect information. The latter is to slow for my requirements and doesn't take daylight saving time into account.

I created a function that is quicker and does take daylight saving into account. You can specify business hours and holidays.

Usage

The function accepts 5 arguments. 3 of them are optional.

  1. The start time stamp (POSIXct)
  2. The end time stamp (POSIXct)
  3. The starting time of a business day (string: '00:00' to '24:00')
  4. The ending time of a business day (string: '00:00' to '24:00')
  5. A list of holidays (list of dates (as.Date))

Example:

start <- as.POSIXct('2014-09-27 10:12:37', tz = 'Europe/Amsterdam')
end <- as.POSIXct('2016-12-10 20:04:18', tz = 'Europe/Amsterdam')

biz_hrs(start, end, '10:00', '17:00')

You can also run it on dataframe columns. Make sure that every value is formatted properly. If an end value is older than a start value, the function returns NA. To change that edit line 6

Code

library(lubridate)


biz_hrs <- Vectorize(function(start, end, starting_time = '9:00', ending_time = '17:00', holidays = NULL){


      if(end < start){

        return(NA)

      } else {

        start_datetime <- as.POSIXct(paste0(substr(start,1,11), starting_time, ':00'))
        end_datetime <- as.POSIXct(paste0(substr(end,1,11), ending_time, ':00'))

        if(as.Date(start) == as.Date(end) & !as.Date(start) %in% holidays & !format(as.Date(start), "%u") %in% c(6,7)){ #if starting time stamp is on same day as ending time stamp and if day is not a holiday or weekend

          if(start > start_datetime & end < end_datetime){ #if starting time stamp is later than start business hour and ending time stamp is earlier then ending business hour.
            return(as.numeric(difftime(end, start), units = 'hours'))
          } else if(start > start_datetime & end > end_datetime & start < end_datetime){ #if starting time stamp is later than end business hour and ending time stamp is earlier then ending business hour.
            return(as.numeric(difftime(as.POSIXct(paste0(substr(start,1,11), ending_time, ':00')), start), units = 'hours'))
          } else if(start < start_datetime & end < end_datetime & end > start_datetime){ #if starting time stamp is earlier than end business hour and ending time stamp is later than starting business hour.
            return(as.numeric(difftime(end, start_datetime), units = 'hours'))
          } else if(start > end_datetime & end > end_datetime){ #if starting time stamp is later than end business hour and ending time stamp is later than ending business hour.
            return(0)
          } else if(start < start_datetime & end < start_datetime){ #if starting time stamp is earlier than start business hour and ending time stamp is earlier than starting business hour.
            return(0)
          } else {
            return(as.numeric(difftime(end_datetime, start_datetime), units = 'hours'))
          }

        } else { #if starting time stamp and ending time stamp occured on a different day.

          business_hrs <- as.numeric(difftime(as.POSIXct(paste0('2017-01-01', ending_time, ':00')),
                                              as.POSIXct(paste0('2017-01-01', starting_time, ':00')) #calculate business hours range by specified parameters
          ), units = 'hours')

          start_day_hrs <- ifelse(start < as.POSIXct(paste0(substr(start,1,11), ending_time, ':00')) & !as.Date(start) %in% holidays & !format(as.Date(start), "%u") %in% c(6,7), #if start time stamp is earlier than specified ending time
                                  as.numeric(difftime(as.POSIXct(paste0(substr(start,1,11), ending_time, ':00')), start), units = 'hours'), #calculate time between time stamp and specified ending time
                                  0 #else set zero
          ) #calculate amount of time on starting day
          start_day_hrs <- pmin(start_day_hrs, business_hrs) #cap the maximum amount of hours dertermined by the specified business hours
          start_day_hrs
          end_day_hrs <- ifelse(end > as.POSIXct(paste0(substr(end,1,11), starting_time, ':00')) & !as.Date(end) %in% holidays & !format(as.Date(end), "%u") %in% c(6,7), #if end time stamp is later than specified starting time
                                as.numeric(difftime(end, as.POSIXct(paste0(substr(end,1,11), starting_time, ':00'))), units = 'hours'), #calculate time between time stamp and specified starting time
                                0) #calculate amount of time on ending day
          end_day_hrs <- pmin(end_day_hrs, business_hrs) #cap the maximum amount of hours dertermined by the specified business hours
          days_between <- seq(as.Date(start), as.Date(end), by = 1) #create a vector of dates (from and up to including) the starting time stamp and ending time stamp
          business_days <- days_between[!days_between %in% c(as.Date(start), as.Date(end)) & !days_between %in% holidays & !format(as.Date(days_between), "%u") %in% c(6,7)] #remove weekends and holidays from vector of dates

          return(as.numeric(((length(business_days) * business_hrs) + start_day_hrs + end_day_hrs))) #multiply the remaining number of days in the vector (business days) by the amount of business hours and add hours from the starting and end day. Return the result

        }

      }


    })
like image 102
Franck Avatar answered Jan 20 '26 16:01

Franck



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!