You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何计算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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.25 05:15:39