PHP中计算排除节假日与周末的有效工作时长(秒级)实现咨询
PHP计算符合工作规则的时长实现思路与代码
核心思路拆解
要计算指定时间区间内符合「周一至周五9:00-17:00+排除指定节假日」的有效时长,可按以下步骤处理:
- 标准化时间格式:将输入的开始/结束时间转换为
DateTime对象,统一处理时区(建议用业务时区避免偏差)。 - 定义规则常量:明确工作日范围(周一到周五,对应
DateTime::format('N')返回1-5)、每日工作时长(9:00到17:00,共28800秒)、排除的节假日列表(转成Y-m-d格式的字符串数组)。 - 分场景计算时长:
- 若开始/结束时间在同一天:直接判断当天是否为有效工作日,再计算当天内的有效时段差值。
- 若跨多天:拆分三部分计算:开始当天的有效时长、中间完整天数的有效时长、结束当天的有效时长,最后求和。
代码实现
function calculateWorkDuration(string $startTime, string $endTime): int { // 定义规则 $workStartHour = '09:00:00'; $workEndHour = '17:00:00'; $holidays = [ '2023-08-15', '2023-11-01', '2023-11-11', '2023-12-25' ]; $dailyWorkSeconds = 8 * 3600; // 9-17点共8小时 // 转换为DateTime对象(假设输入为Y-m-d H:i:s格式,可根据实际调整createFromFormat参数) $start = new DateTime($startTime); $end = new DateTime($endTime); // 处理开始时间晚于结束时间的异常情况 if ($start > $end) { return 0; } $totalSeconds = 0; // 情况1:开始和结束在同一天 if ($start->format('Y-m-d') === $end->format('Y-m-d')) { $dateStr = $start->format('Y-m-d'); // 判断是否为工作日且非节假日 if ($start->format('N') >= 1 && $start->format('N') <=5 && !in_array($dateStr, $holidays)) { $workStart = new DateTime($dateStr . ' ' . $workStartHour); $workEnd = new DateTime($dateStr . ' ' . $workEndHour); // 确定实际计算的起始和结束点 $actualStart = max($start, $workStart); $actualEnd = min($end, $workEnd); if ($actualStart < $actualEnd) { $totalSeconds = $actualEnd->getTimestamp() - $actualStart->getTimestamp(); } } return $totalSeconds; } // 情况2:跨多天,先计算开始当天的有效时长 $startDateStr = $start->format('Y-m-d'); if ($start->format('N') >=1 && $start->format('N') <=5 && !in_array($startDateStr, $holidays)) { $workEnd = new DateTime($startDateStr . ' ' . $workEndHour); if ($start < $workEnd) { $actualStart = max($start, new DateTime($startDateStr . ' ' . $workStartHour)); $totalSeconds += $workEnd->getTimestamp() - $actualStart->getTimestamp(); } } // 计算结束当天的有效时长 $endDateStr = $end->format('Y-m-d'); if ($end->format('N') >=1 && $end->format('N') <=5 && !in_array($endDateStr, $holidays)) { $workStart = new DateTime($endDateStr . ' ' . $workStartHour); if ($end > $workStart) { $actualEnd = min($end, new DateTime($endDateStr . ' ' . $workEndHour)); $totalSeconds += $actualEnd->getTimestamp() - $workStart->getTimestamp(); } } // 计算中间完整天数的有效时长 $currentDate = clone $start; $currentDate->modify('+1 day'); while ($currentDate->format('Y-m-d') < $endDateStr) { $dateStr = $currentDate->format('Y-m-d'); if ($currentDate->format('N') >=1 && $currentDate->format('N') <=5 && !in_array($dateStr, $holidays)) { $totalSeconds += $dailyWorkSeconds; } $currentDate->modify('+1 day'); } return $totalSeconds; } // 测试示例:2023-10-28 07:00:00 到 2023-11-03 10:00:00 $duration = calculateWorkDuration('2023-10-28 07:00:00', '2023-11-03 10:00:00'); echo "有效时长(秒):" . $duration; // 输出90000(3天*8小时 + 1小时 = 25小时 = 90000秒)
关键细节说明
- 时区处理:如果业务涉及跨时区,建议在创建
DateTime对象时指定时区(如new DateTime($startTime, new DateTimeZone('Asia/Shanghai'))),避免时间偏差。 - 节假日扩展:若节假日需要动态获取,可将
$holidays数组替换为数据库查询或内部接口返回的结果。 - 边界情况覆盖:代码处理了开始时间晚于结束时间、当天完全在工作时段外、节假日刚好在工作日等场景。
内容的提问来源于stack exchange,提问作者executable
相关产品推荐
相关产品推荐

