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

如何按时间与类型分组考勤数据?含夜班跨天场景处理

考勤数据跨天分组问题及解决方案

问题描述

我正在开发考勤数据分组功能,碰到了以下问题:

  • 同天的上下班记录分组正常
  • 夜班跨天场景(比如22:00上班、次日06:00下班)需要单独分组
  • 次日晚于该夜班下班时间的考勤记录,要归为新组

考勤数据示例

$attendances = [
    // 第1组
    [
        "id" => 1,
        "考勤日期" => "2023-08-24",
        "时间" => "11:00:00",
        "状态" => "上班"
    ],
    [
        "id" => 2,
        "考勤日期" => "2023-08-24",
        "时间" => "14:00:00",
        "状态" => "下班"
    ],
    // 第2组(跨天夜班)
    [
        "id" => 3,
        "考勤日期" => "2023-08-24",
        "时间" => "22:00:00",
        "状态" => "上班"
    ],
    [
        "id" => 4,
        "考勤日期" => "2023-08-25",
        "时间" => "06:00:00",
        "状态" => "下班"
    ],
    // 第3组
    [
        "id" => 5,
        "考勤日期" => "2023-08-25",
        "时间" => "10:00:00",
        "状态" => "上班"
    ],
    [
        "id" => 6,
        "考勤日期" => "2023-08-25",
        "时间" => "18:00:00",
        "状态" => "下班"
    ]
];

当前仅部分场景有效的代码

$当前日期 = null;
$分组后的考勤 = [];
$当前分组 = [];

foreach ($attendances as $考勤记录) {
    if ($考勤记录->考勤日期 !== $当前日期) {
        // 遇到新日期,创建新分组
        $当前日期 = $考勤记录->考勤日期;
        if (!empty($当前分组)) {
            $分组后的考勤[] = $当前分组;
        }
        $当前分组 = [$考勤记录];
    } else {
        // 添加到当前分组
        $当前分组[] = $考勤记录;
    }
}

if (!empty($当前分组)) {
    $分组后的考勤[] = $当前分组;
}

修正后的解决方案代码

$分组后的考勤 = [];
$当前分组 = [];
$上一次下班时间 = null;

foreach ($attendances as $考勤记录) {
    // 将日期和时间拼接成完整的时间对象,方便跨天比较
    $当前时间 = new DateTime($考勤记录['考勤日期'] . ' ' . $考勤记录['时间']);
    
    if (empty($当前分组)) {
        $当前分组[] = $考勤记录;
        continue;
    }
    
    $最后一条记录 = end($当前分组);
    $最后记录时间 = new DateTime($最后一条记录['考勤日期'] . ' ' . $最后一条记录['时间']);
    
    if ($最后一条记录['状态'] === '下班') {
        // 上一条是下班,新记录必须开新组
        if ($上一次下班时间 && $当前时间 > $上一次下班时间) {
            $分组后的考勤[] = $当前分组;
            $当前分组 = [$考勤记录];
        } else {
            $当前分组[] = $考勤记录;
        }
        $上一次下班时间 = $当前时间;
    } else {
        // 上一条是上班,判断当前是否是对应的下班
        if ($考勤记录['状态'] === '下班') {
            // 下班时间晚于上班时间(跨天也能正确判断),加入当前组
            if ($当前时间 > $最后记录时间) {
                $当前分组[] = $考勤记录;
                $上一次下班时间 = $当前时间;
            } else {
                // 异常情况:下班时间早于上班,单独开组
                $分组后的考勤[] = $当前分组;
                $当前分组 = [$考勤记录];
            }
        } else {
            // 连续两条上班记录,直接开新组
            $分组后的考勤[] = $当前分组;
            $当前分组 = [$考勤记录];
        }
    }
}

// 添加最后一个未完成的分组
if (!empty($当前分组)) {
    $分组后的考勤[] = $当前分组;
}

代码逻辑说明

  1. 用DateTime对象处理时间,无需手动计算跨天差值,直接就能比较时间先后
  2. 跟踪上一次下班时间,确保次日晚于该时间的考勤会被分到新组
  3. 根据当前分组最后一条记录的状态(上班/下班),判断是否需要开启新分组
  4. 兼容异常数据场景(比如连续上班、下班时间早于上班)

内容的提问来源于stack exchange,提问作者ador

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.12 12:35:58