【问题标题】:How to count business hour between timestamps excluding weekend in MATLAB?如何在 MATLAB 中计算除周末之外的时间戳之间的营业时间?
【发布时间】:2017-11-27 04:40:03
【问题描述】:

我想知道是否有人知道如何计算MATLAB 中时间戳之间的营业时间或分钟?假设我有两个时间戳:

started: 22/06/2017 18:00
ended: 26/06/2017 09:00

我想计算08:0017:00 之间的分钟数,不包括周末。应该是600 分钟。

有谁知道如何编写MATLAB 函数来做到这一点? 我也可以使用python 和/或R

新请求
如果每个工作日的工作时间更改为9:30-11:3013:00-15:00。不知道有没有人知道怎么弄?

提前致谢!

更新
@gnovice 我对您的原始代码进行了一些更改。它适用于使用两个单独的范围!想知道你对此有何感想?

function workMins = work_time(startTime, endTime)
  dateBlock = repmat((dateshift(startTime, 'start', 'day'):...
                      dateshift(endTime, 'start', 'day')).', 1, 4); %'
  dateBlock(:, 1) = dateBlock(:, 1)+hours(9)+minutes(30);
  dateBlock(:, 2) = dateBlock(:, 2)+hours(11)+minutes(30);
  dateBlock(:, 3) = dateBlock(:, 3)+hours(13)+minutes(00);
  dateBlock(:, 4) = dateBlock(:, 4)+hours(15);  
  dateBlock(1, 1) = max(dateBlock(1, 1), startTime);
  dateBlock(1, 3) = max(dateBlock(1, 3), startTime);  
  dateBlock(end, 2) = min(dateBlock(end, 2), endTime);
  dateBlock(end, 4) = min(dateBlock(end, 4), endTime);  
  dateBlock((datestr(dateBlock(:, 1), 'd') == 'S'), :) = [];
  workMins = max(diff(dateBlock, 1, 2), 0);
  workMins(:,2) = [];
  workMins = minutes(sum(workMins(:)));
end

【问题讨论】:

  • 除非您有 MATLAB 的金融工具箱(或第三方库),否则在 Python 或 R 中执行此操作可能更容易。
  • 你知道如何在 Python 或 R 中做到这一点吗?
  • 从技术上讲,您可以自己编写。应该不会太难

标签: python r matlab


【解决方案1】:

MATLAB 解决方案并不像大家想象的那么难看(尽管使用Financial Toolbox 可能会对其进行简化)。切入正题,这里是一个接受两个 datetime 值的函数的解决方案:

function workMins = work_time(startTime, endTime)
  dateBlock = repmat((dateshift(startTime, 'start', 'day'):...
                      dateshift(endTime, 'start', 'day')).', 1, 2); %'
  dateBlock(:, 1) = dateBlock(:, 1)+hours(8);
  dateBlock(:, 2) = dateBlock(:, 2)+hours(17);
  dateBlock(1, 1) = max(dateBlock(1, 1), startTime);
  dateBlock(end, 2) = min(dateBlock(end, 2), endTime);
  dateBlock((datestr(dateBlock(:, 1), 'd') == 'S'), :) = [];
  workMins = minutes(sum(max(diff(dateBlock, 1, 2), 0)));
end

这就是它的工作原理......

首先,我们将开始和结束时间设置为 datetime 值:

startTime = datetime('22/06/2017 18:00');
endTime = datetime('26/06/2017 09:00');

现在我们可以创建一个N-by-2 日期时间矩阵。每行将涵盖从startTimeendTime 范围内的一天,时间现在设置为0:00:00

dateBlock = repmat((dateshift(startTime, 'start', 'day'):...
                    dateshift(endTime, 'start', 'day')).', 1, 2);

现在我们将第一列时间设置为8:00:00,将第二列时间设置为17:00:00

dateBlock(:, 1) = dateBlock(:, 1)+hours(8);
dateBlock(:, 2) = dateBlock(:, 2)+hours(17);

现在我们分别添加startTimeendTime 作为第一个和最后一个元素,将它们剪辑到8:00:0017:00:00 时间范围:

dateBlock(1, 1) = max(dateBlock(1, 1), startTime);
dateBlock(end, 2) = min(dateBlock(end, 2), endTime);

接下来,我们删除 datestr 日值为 'S'(即周末)的行:

dateBlock((datestr(dateBlock(:, 1), 'd') == 'S'), :) = [];

最后,我们对列的差异求和(忽略负值),然后转换为分钟:

workMins = minutes(sum(max(diff(dateBlock, 1, 2), 0)));

我们得到了想要的结果:

workMins =

   600

编辑:

关于新请求,您可以稍微更改函数以允许传递工作日的开始和结束时间,如下所示:

function workMins = work_time(startTime, endTime, workDayStart, workDayEnd)
  dateBlock = repmat((dateshift(startTime, 'start', 'day'):...
                      dateshift(endTime, 'start', 'day')).', 1, 2); %'
  dateBlock(:, 1) = dateBlock(:, 1)+hours(workDayStart);
  dateBlock(:, 2) = dateBlock(:, 2)+hours(workDayEnd);
  ...

现在您可以使用单独的工作时间范围调用它并添加结果:

startTime = datetime('22/06/2017 18:00');
endTime = datetime('26/06/2017 09:00');
workTotal = work_time(startTime, endTime, 9.5, 11.5) ...
            + work_time(startTime, endTime, 13, 15);

或者从一个较大的范围中减去一个子范围:

workTotal = work_time(startTime, endTime, 9.5, 15) ...
            - work_time(startTime, endTime, 11.5, 13);

如果您希望将工作日时间指定为字符数组,您可以编写如下函数:

function workMins = work_time(startTime, endTime, workDayStart, workDayEnd)
  dateBlock = repmat((dateshift(startTime, 'start', 'day'):...
                      dateshift(endTime, 'start', 'day')).', 1, 2); %'
  workDayStart = datevec(workDayStart);
  workDayEnd = datevec(workDayEnd);
  dateBlock(:, 1) = dateBlock(:, 1)+duration(workDayStart(4:6));
  dateBlock(:, 2) = dateBlock(:, 2)+duration(workDayEnd(4:6));
  ...

并像这样使用它:

workTotal = work_time(startTime, endTime, '9:30', '11:30') ...
            + work_time(startTime, endTime, '13:00', '15:00');

【讨论】:

  • 非常感谢!终于有一个在 MATLAB 中了!而且效果很好!干净整洁!
  • 您的代码运行良好!但我现在需要将工作时间计算为:工作日的 9:30-11:30 和 13:00-15:00。我想知道这段代码是否也可以做到这一点?提前致谢。
  • @James:那么,您想使用两个单独的范围,而不是 8:00-17:00 的单个范围?
  • 是的!我想知道你是否知道如何操纵代码来做到这一点??
  • 感谢您的回答!它运作良好!我还在上述问题中更新了我的解决方案。
【解决方案2】:

这不漂亮,必须有一种更经典的方式,但这应该在基础 R 中解决问题:

x <- seq(from = as.POSIXct("22/06/2017-18:00", format = '%d/%m/%Y-%H:%M'), to = as.POSIXct("26/06/2017-09:00", format = '%d/%m/%Y-%H:%M'), by= (60*60))
x <- x[!weekdays(x) %in% c('Saturday','Sunday')]
x <- x[x %in% as.POSIXct(unlist(lapply(unique(as.Date(x)), function(x){paste0(x, ' ', ifelse(nchar(8:16) == 1, as.character(paste0('0', 8:16)), 8:16))})), format = "%Y-%m-%d %H")]
(length(x)-1)*60

考虑一下,生成具有所需时间跨度的工作日并将每天的 difftime 的结果相加可能会更聪明。

【讨论】:

    【解决方案3】:

    这是R中的解决方案

    数据

    start = "22/06/2017 18:00"
    end = "26/06/2017 09:00"
    

    功能

    foo = function(start, end, filter_weekend = TRUE,
            daystart = "08:00:00", dayend = "17:00:00", units = "mins"){
    #start and end should be POSIXct
    
        require(lubridate)
    
        #Get a sequence of all dates between start and end
        df = data.frame(col1 = seq.Date(from = as.Date(start),
                                        to = as.Date(end),
                                        by = "days"))
    
        #Set start of each day at daystart
        df$start = ymd_hms(paste(df$col1, daystart))
    
        #Set end of each day at dayend
        df$end = ymd_hms(paste(df$col1, dayend))
        df$days = weekdays(df$start)
    
        #Set the first day$start to be correct date and time
        df$start[1] = start
        #Set the last day$end to be correct date and time
        df$end[NROW(df)] = end
    
        if (filter_weekend == TRUE){
            #Remove weekends
            df = df[!df$days %in% c("Saturday", "Sunday"), ]
        }
        #Remove rows if end is smaller than start (relevant in first and last row)
        df = df[df$end > df$start, ]
    
        #Compute time difference for each row
        df$time = difftime(time1 = df$end, time2 = df$start, units = units)
    
        #Output
        return(sum(df$time))
    }
    

    用法

    library(lubridate)
    foo(start = dmy_hm(start), end = dmy_hm(end))
    #Time difference of 600 mins
    
    foo(start = dmy_hms("22/06/2017 14:21:19"),
            end = dmy_hms("26/06/2017 06:20:53"),
            units = "secs", filter_weekend = TRUE)
    #Time difference of 41921 secs
    

    【讨论】:

    • 这很好!但是你也可以计算分钟和秒吗?
    • 我的意思是如果结束时间为 end = "26/06/2017 09:59",该功能是否仍然有效?
    【解决方案4】:

    这是一个自定义函数,用于计算工作分钟数。它有点冗长,但其中有一些检查有助于确保正确计算值。它还将单位作为参数,因此您可以将总数计算为秒、分钟、小时、天或周。

    work.time <- function(Date1, Date2, work.start, work.end, unit){
    
      # If dates are equal return 0
      if(Date1 == Date2){
        return(0)
      }
    
      # Check to make sure Date1 is always before Date2
      if(Date1 > Date2){
        D <- Date1
        Date1 <- Date2
        Date2 <- D
        rm(D)
      }
    
      # Get workday start and end timestamps for both days
      D1.start <- as.POSIXct(format(Date1, format = paste("%d/%m/%Y", work.start)), format = "%d/%m/%Y %H:%M")
      D1.end <- as.POSIXct(format(Date1, format = paste("%d/%m/%Y", work.end)), format = "%d/%m/%Y %H:%M")
    
      D2.start <- as.POSIXct(format(Date2, format = paste("%d/%m/%Y", work.start)), format = "%d/%m/%Y %H:%M")
      D2.end <- as.POSIXct(format(Date2, format = paste("%d/%m/%Y", work.end)), format = "%d/%m/%Y %H:%M")
    
      # Calculate value for a full workday
      full.day <- as.numeric(difftime(D1.end, D1.start, units = unit))
    
      # Calculate value if dates fall on the same day
      if(as.Date(Date1) == as.Date(Date2)){
        if(weekdays(as.Date(Date1)) %in% c("Saturday", "Sunday")){
          val <- 0
        } else if(Date1 >= D1.start & Date2 <= D1.end){
          val <- as.numeric(difftime(Date2, Date1, units = unit))
        } else if(Date1 >= D1.start & Date2 > D1.end){
          val <- as.numeric(difftime(D1.end, Date1, units = unit))
        } else if(Date1 < D1.start & Date2 <= D1.end){
          val <- as.numeric(difftime(Date2, D1.start, units = unit))
        } else if(Date1 < D1.start & Date2 > D1.end){
          val <- full.day
        } else{
          val <- 0
        }
        return(val)
      }
    
      # Calculate value for first workday
      if(weekdays(as.Date(Date1)) %in% c("Saturday", "Sunday")){
        d1.val <- 0
      } else 
        if(Date1 >= D1.start & Date1 <= D1.end){
        d1.val <- as.numeric(difftime(D1.end, Date1, units = unit))
      } else if(Date1 > D1.end){
        d1.val <- 0
      } else if(Date1 < D1.start){
        d1.val <- full.day
      }
    
      # Calculate value for last workday
      if(weekdays(as.Date(Date2)) %in% c("Saturday", "Sunday")){
        d2.val <- 0
      } else if(Date2 >= D2.start & Date2 <= D2.end){
        d2.val <- as.numeric(difftime(Date2, D2.start, units = unit))
      } else if(Date2 > D2.end){
        d2.val <- full.day
      } else if(Date2 < D2.start){
        d2.val <- 0
      }
    
      # Calculate work value for all workdays between Date1 and Date2
      work.days <- sum(!weekdays(seq(as.Date(Date1) + 1, as.Date(Date2) - 1, "days")) %in% c("Saturday", "Sunday"))
    
      # Add up final work value total
      work.val <- (work.days * full.day) + d1.val + d2.val
    
      return(work.val)
    }
    
    
    Date1 <- as.POSIXct("24/06/2017 18:00", format = "%d/%m/%Y %H:%M")
    Date2 <- as.POSIXct("26/06/2017 09:00", format = "%d/%m/%Y %H:%M") 
    
    work.time(Date1, Date2, work.start = "08:00", work.end = "17:00", unit = "mins")
    

    【讨论】:

    • 只是想更新这个脚本。我还添加了一张支票,以考虑 Date1 或 Date2 是否在周末。
    猜你喜欢
    • 2012-11-05
    • 2018-12-26
    • 2022-01-01
    • 2020-03-31
    • 2019-07-14
    • 2013-05-28
    • 1970-01-01
    • 2020-11-18
    • 2019-10-09
    相关资源
    最近更新 更多