【问题标题】:How to extract Business Hours between two different times values如何在两个不同的时间值之间提取营业时间
【发布时间】:2015-12-03 14:07:01
【问题描述】:

为了评估由服务台处理的工单,我想知道工单有多少营业时间有效。我可以轻松地减去时间并获得总小时数。但唯一应该计算的时间是 08:30 到 18:00 之间。

例如:如果工单在 11/23/2015 10:20 创建并在 11/24/2015 17:20 完成,则 31 个“正常”小时已过去。我只对已经过去的营业时间(8:30 到 18:00)感兴趣;在这种情况下16 hours and 30 minutes

【问题讨论】:

  • 你应该给我们一些数据和预期的输出。
  • 假设一张工单在 2015 年 11 月 23 日 10:20 创建并在 2015 年 11 月 24 日 17:20 完成,已经过去了 31 个“正常”小时。但我只对已经过去的营业时间感兴趣。所以在这种情况下是 16 小时 30 分钟。我目前有一个 .csv 文件,其中列日期创建和日期以此处使用的格式完成。

标签: r datetime time


【解决方案1】:

我已经尝试过@mvan 和@Steffan Jansson 的解决方案。不幸的是,它们都不适合我的需要。第一个返回不正确的信息。后者是为了满足我的要求而放慢速度,并且没有考虑夏令时。

我创建了一个更快的函数,并且确实考虑了夏令时。您可以指定营业时间和节假日。

用法

该函数接受 5 个参数。其中 3 个是可选的。

  1. 开始时间戳 (POSIXct)
  2. 结束时间戳 (POSIXct)
  3. 工作日的开始时间(字符串:'00:00' 到 '24:00')
  4. 一个工作日的结束时间(字符串:'00:00' to '24:00')
  5. 假期列表(日期列表 (as.Date))

示例:

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

您也可以在数据框列上运行它。确保每个值的格式都正确。如果结束值早于开始值,则函数返回 NA。更改该编辑行 6

代码

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

        }

      }


    })

【讨论】:

    【解决方案2】:

    我最近遇到了类似的需求,并根据上面的 @mvan 脚本创建了一个包。 https://github.com/janzzon/difftimeOffice
    输入需要为 POSIX 时间。
    输出以秒为单位。
    通过参数working_hours = c(8.5, 18)指定帮助台开放时间。
    (还)没有实施额外的假期。

    【讨论】:

      【解决方案3】:
      library(lubridate)
      
         tickets <- data.frame(open = as.POSIXct(strptime(df$open, "%m/%d/%Y %H:%M")), 
                            closed = as.POSIXct(strptime(df$closed, "%m/%d/%Y %H:%M"))
      
      
      excludeDayCount <- Vectorize(function(open, close) {
      
         # Check if the ticket was open and closed on the same day
         if (identical(as.Date(open), as.Date(close))) return (0)
      
         # All the holidays to be excluded need to be put here
         holidays <- as.POSIXct(strptime(c("12/24/2015", "12/25/2015"), 
                                        "%m/%d/%Y"))
      
         # Dates between open and close  
         day_seq <- floor_date(seq(open + days(1), close, by = "days"), "day")
      
         # Count holidays / weekend days
         return(sum(day_seq %in% holidays | wday(day_seq) %in% c(1,7)))
      
      })
      
      bizHrDiff <- function(open, close) {
      
          # Hours from the end of one work day until the start of another
          hours_between_days <- dhours(6) + dhours(8.5)
      
          # Number of days to exclude
          excl_days <- excludeDayCount(open, close)  
          # Number of days in include
          reg_days <- as.integer(as.Date(close) - as.Date(open)) - excl_days 
      
      
      
          # Total duration between dates
            span <- as.duration(interval(open, close))
            # Remove the number of holidays and weekends
            span <- span - ddays(excl_days)
            # Remove out of office hours
            span <- span - (reg_days * hours_between_days)
      
      
      
      
           # Return in hours
            return(time_length(span, unit = "hour"))
      
      
      }
      
      bizHrDiff(tickets$open, tickets$closed)
      

      【讨论】:

      • 感谢 mvan 的脚本。当我问我的问题时,我可能不够彻底。因为我们的服务台在周末和国定假日期间关闭。我在查看您的脚本时还有一个问题。当我将我的 csv 文件添加到数据框时,此脚本会自动计算文件中所有工单的营业时间吗?
      • @M.Vennemans 试试这个更新的代码。您可能希望围绕它为所有极端情况构建单元测试,因为这些情况不断出现。我还假设您不会在办公时间以外打开/关闭门票,这需要更多的调整。
      • 再次感谢您的脚本。当我完全运行您的代码时,它可以完美运行。但是由于我的票很多,无法在代码中一一输入,所以我将一个csv文件加载到R中。但是当我再次执行代码时,它不会返回任何内容。你可能知道我做错了什么吗?最近两天我一直在玩它,但到目前为止我无法解决问题。 (我对 R 还是很陌生,因此感谢所有帮助:))
      • @M.Vennemans 抱歉,我需要更多信息。你遇到了什么错误?可以分享几行文件吗?我怀疑您需要使用 df$open &lt;- as.POSIXct(strptime(df$open, "%m/%d/%Y %H:%M")) 之类的东西转换包含日期的列,并且在关闭时间也一样,但这只是一个猜测
      • 这是我用来读取数据框的内容(341 obs。2 个变量(打开、关闭)。数据框称为“ticketsopenclose”。ticketsopenclose
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2020-11-18
      • 1970-01-01
      • 2019-08-04
      • 2021-12-11
      • 1970-01-01
      • 2020-03-31
      • 2020-05-17
      相关资源
      最近更新 更多