如何计算09:00-17:00工作时段内外的工时时长(PHP实现)
基于Carbon库计算工作时段内时长与加班时长
你可以通过计算实际工作区间与固定工作时段(09:00-17:00)的交集来区分工作时长和加班时长,核心思路是:先算出总工作时长,再减去工作时段内的重叠时长,得到加班时长。
基础实现(单天工作场景)
直接替换你代码中的注释部分,以下是完整的补充逻辑:
foreach ($timeSheets as $key => $time) { if ($time->is_in == 1) { $time_in = new Carbon($time->event_at); $time_out = new Carbon($timeSheets[$key + 1]->event_at); [$time_in, $time_out] = $this->getTimeZoneName($bill->BillingRateTimezone->timezone, $time_in, $time_out); // 1. 创建当天工作时段的起止时间(基于打卡日期) $workDayStart = $time_in->copy()->setTime(9, 0); $workDayEnd = $time_in->copy()->setTime(17, 0); // 2. 计算实际工作的总时长(单位:秒) $totalWorkSeconds = $time_in->diffInSeconds($time_out); // 3. 计算工作时段与实际工作区间的重叠部分 $overlapStart = max($time_in, $workDayStart); $overlapEnd = min($time_out, $workDayEnd); // 4. 计算工作时段内的时长(若没有重叠则为0) $sumOfInside = $overlapStart->lt($overlapEnd) ? $overlapStart->diffInSeconds($overlapEnd) : 0; // 5. 加班时长 = 总时长 - 工作时段内时长 $sumOfOutside = $totalWorkSeconds - $sumOfInside; // 可选:转成易读的格式,比如"2小时30分钟" $insideDuration = \Carbon\CarbonInterval::seconds($sumOfInside)->cascade()->forHumans(); $outsideDuration = \Carbon\CarbonInterval::seconds($sumOfOutside)->cascade()->forHumans(); } }
进阶实现(跨天工作场景)
如果用户存在跨天工作的情况(比如当天22:00到次日08:00),需要逐天计算工作时段的重叠时长:
foreach ($timeSheets as $key => $time) { if ($time->is_in == 1) { $time_in = new Carbon($time->event_at); $time_out = new Carbon($timeSheets[$key + 1]->event_at); [$time_in, $time_out] = $this->getTimeZoneName($bill->BillingRateTimezone->timezone, $time_in, $time_out); $sumOfInside = 0; $currentDate = $time_in->copy()->startOfDay(); $endDate = $time_out->copy()->startOfDay(); // 遍历工作覆盖的每一天 while ($currentDate->lte($endDate)) { // 当天的工作时段起止 $dailyWorkStart = $currentDate->copy()->setTime(9, 0); $dailyWorkEnd = $currentDate->copy()->setTime(17, 0); // 当天实际工作的起止(取与全局工作时间的交集) $dailyActualStart = max($time_in, $dailyWorkStart); $dailyActualEnd = min($time_out, $dailyWorkEnd); // 累加当天工作时段内的时长 if ($dailyActualStart->lt($dailyActualEnd)) { $sumOfInside += $dailyActualStart->diffInSeconds($dailyActualEnd); } $currentDate->addDay(); } // 计算总时长和加班时长 $totalWorkSeconds = $time_in->diffInSeconds($time_out); $sumOfOutside = $totalWorkSeconds - $sumOfInside; } }
关键逻辑说明
- 使用
copy()避免修改原始的打卡时间对象 - 通过
max()和min()快速定位两个时间区间的重叠部分 - 用秒数作为计算单位保证精度,最后可按需转成小时/分钟格式
内容的提问来源于stack exchange,提问作者reza baghiee
相关产品推荐
相关产品推荐

