Perl中基于业务日历计算添加指定小时后的工作日
Perl 实现基于自定义业务日历的小时累加功能
核心实现思路
- 借助
DateTime系列模块处理日期时间的解析与运算,避免手动处理日期边界、时区等复杂问题 - 逐天消耗需累加的小时数:
- 先判断当前日期是否为有效工作日(非周末、不在节假日列表内)
- 若为工作日,计算当天剩余可工作时长,优先消耗剩余小时数;若剩余小时数超过当天剩余时长,扣除当天时长后跳转到下一个工作日的开始时间继续处理
- 若为非工作日,直接跳转到下一个工作日的开始时间,不消耗小时数
依赖模块
先安装必要的CPAN模块:
cpanm DateTime DateTime::Format::Strptime
代码实现
假设你的业务日历哈希结构如下(可根据实际定义调整):
# 与你已有的%sansSLA、%sla8_18、%slaMax对应示例结构 my %sla8_18 = ( work_period => { start => '08:00', end => '18:00' }, holidays => [ '2022-01-01', '2022-10-01' # 其他节假日... ] );
核心功能函数:
use strict; use warnings; use DateTime; use DateTime::Format::Strptime; # 解析日期字符串为DateTime对象 sub parse_datetime { my ($dt_str) = @_; my $parser = DateTime::Format::Strptime->new( pattern => '%Y-%m-%d %H:%M', time_zone => 'local', # 可按需改为UTC或其他时区 on_error => 'croak', ); return $parser->parse_datetime($dt_str); } # 判断日期是否为有效工作日 sub is_business_day { my ($dt, $calendar) = @_; # 排除周末(周六=6,周日=7) return 0 if $dt->day_of_week >= 6; # 排除节假日 my $date_str = $dt->ymd; foreach my $holiday (@{$calendar->{holidays}}) { return 0 if $holiday eq $date_str; } return 1; } # 按业务日历累加小时数 sub add_business_hours { my ($start_dt_str, $hours_to_add, $calendar) = @_; my $current_dt = parse_datetime($start_dt_str); my $remaining_hours = $hours_to_add; # 解析业务时段为分钟数,方便计算 my ($work_start_h, $work_start_m) = split /:/, $calendar->{work_period}->{start}; my ($work_end_h, $work_end_m) = split /:/, $calendar->{work_period}->{end}; my $work_start_min = $work_start_h * 60 + $work_start_m; my $work_end_min = $work_end_h * 60 + $work_end_m; my $daily_work_min = $work_end_min - $work_start_min; while ($remaining_hours > 0) { # 跳转到下一个有效工作日(如果当前不是) until (is_business_day($current_dt, $calendar)) { $current_dt->add(days => 1); $current_dt->set(hour => $work_start_h, minute => $work_start_m); } # 计算当前时间在当天工作时段中的剩余时长 my $current_min = $current_dt->hour * 60 + $current_dt->minute; my $remaining_today_min; if ($current_min < $work_start_min) { $remaining_today_min = $daily_work_min; $current_dt->set(hour => $work_start_h, minute => $work_start_m); } elsif ($current_min > $work_end_min) { $current_dt->add(days => 1); $current_dt->set(hour => $work_start_h, minute => $work_start_m); next; } else { $remaining_today_min = $work_end_min - $current_min; } my $remaining_min = $remaining_hours * 60; if ($remaining_min <= $remaining_today_min) { # 当天剩余时长足够,直接累加 $current_dt->add(minutes => $remaining_min); $remaining_hours = 0; } else { # 当天时长不足,扣除后跳到下一个工作日 $remaining_min -= $remaining_today_min; $remaining_hours = $remaining_min / 60; $current_dt->add(days => 1); $current_dt->set(hour => $work_start_h, minute => $work_start_m); } } return $current_dt->strftime('%Y-%m-%d %H:%M'); }
测试示例
# 示例1:2022-07-18 16:30 添加5小时 my $result1 = add_business_hours('2022-07-18 16:30', 5, \%sla8_18); print "示例1结果:$result1\n"; # 输出:2022-07-19 12:30 # 示例2:2022-07-15 16:30(周五)添加5小时 my $result2 = add_business_hours('2022-07-15 16:30', 5, \%sla8_18); print "示例2结果:$result2\n"; # 输出:2022-07-18 12:30
适配已有哈希结构
如果你的%sansSLA、%sla8_18、%slaMax哈希键名或结构与示例不同,只需调整is_business_day和add_business_hours中读取业务时段、节假日的逻辑即可,比如修改$calendar->{xxx}的键名匹配实际定义。
内容的提问来源于stack exchange,提问作者Jimmy
相关产品推荐
相关产品推荐

