How to extract Business Hours between two different times values

前端 未结 3 1375
走了就别回头了
走了就别回头了 2021-02-06 19:19

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

3条回答
  •  遥遥无期
    2021-02-06 19:35

    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
    
            }
    
          }
    
    
        })
    

提交回复
热议问题