You need to enable JavaScript to run this app. 如何按天计算activities表中checkin/checkout打卡的小时分钟总时长
优惠活动
大模型
产品
解决方案
定价
更多

如何按天计算activities表中checkin/checkout打卡的小时分钟总时长

现有代码存在的核心缺陷
  • 循环嵌套错误:将原本已是活动列表的$activities额外包裹为单层数组传入循环,导致循环仅执行1次,根本没法遍历所有打卡记录
  • 未排序数据:数据库查询返回的活动记录默认无固定时间排序,直接按索引配对打卡类型会出现checkin和checkout完全错位的问题
  • 字段匹配错误:表结构仅包含start时间字段,代码中错误读取不存在的end字段
  • 边界逻辑错误:查询条件设置为start <= 当天23:59,会漏掉当天23:59:00之后的有效打卡记录;同时$d1 = $d1++这类后置自增写法不会修改变量值,索引始终无法推进
  • 无异常兜底:未对连续两次checkin、连续两次checkout、当天最后一次打卡无对应匹配项的异常场景做处理,计算结果会出现偏差
正确计算逻辑

计算规则

  1. 拉取指定员工在目标日期的所有打卡记录,强制按start字段的时间值升序排序
  2. 按顺序遍历打卡列表:遇到类型为checkin的记录时暂存为当前段的开始时间,遇到下一个相邻的checkout记录时,计算两个时间点的时间差,累加到总时长
  3. 若遍历到列表末尾仍有未匹配的checkin记录,直接截断为当天23:59的时间点计算段时长,避免跨天打卡导致的时长错配
  4. 累计所有段的时长后,可换算为总小时、总分钟数值:总分钟换算仅需将累计总秒数除以60即可,和小时计算逻辑完全一致

修正后的PHP实现

function sum_all_hours_by_day($fxemploye_id, $day){
    $hours = 0;
    $date = date_create($day);
    $date_format = date_format($date, 'Y-m-d');
    // 修正查询边界:覆盖当天00:00到次日00:00之前的全量记录
    $day_start = $date_format . " 00:00:00";
    $day_end = date('Y-m-d 00:00:00', strtotime($date_format . " +1 day"));
    // 查询活动并按打卡时间升序排序
    $options = [
        'conditions' => [
            'fxemploye_id = ? and start >= ? and start < ?',
            $fxemploye_id, $day_start, $day_end
        ],
        'order' => 'start ASC'
    ];
    $activities = FxemployeHasActivity::all($options);
    
    $active_checkin_time = null;
    $total_seconds = 0;
    // 直接遍历全量活动列表
    foreach ($activities as $activity) {
        $current_time = strtotime($activity->start);
        $current_type = $activity->type;
        if ($current_type == 'checkin') {
            // 遇到新的checkin,覆盖暂存的开始时间,避免连续checkin的异常
            $active_checkin_time = $current_time;
        } elseif ($current_type == 'checkout' && !is_null($active_checkin_time)) {
            // 匹配到checkout,计算段时长
            $total_seconds += $current_time - $active_checkin_time;
            // 清空暂存的checkin时间
            $active_checkin_time = null;
        }
    }
    // 兜底:如果当天最后有未匹配的checkin,截断到当天结束时间计算
    if (!is_null($active_checkin_time)) {
        $day_end_ts = strtotime($day_end);
        $total_seconds += ($day_end_ts - 1) - $active_checkin_time;
    }
    // 换算为小时(保留2位小数)
    $hours = round($total_seconds / 3600, 2);
    return $hours;
}

可选优化:SQL层直接计算

如果使用的数据库支持窗口函数,可以直接在查询阶段配对相邻打卡记录,减少PHP层的计算开销,核心SQL逻辑为:

SELECT 
  SUM(TIMESTAMPDIFF(SECOND, start, next_start)) / 3600 AS total_hours
FROM (
  SELECT 
    type,
    start,
    LEAD(start) OVER (ORDER BY start ASC) AS next_start
  FROM activities
  WHERE fxemploye_id = 123 
    AND start >= '2020-11-30 00:00:00' 
    AND start < '2020-12-01 00:00:00'
) t
WHERE type = 'checkin';

注意:该SQL仅能计算有明确配对checkout的时段,若需兼容兜底逻辑需额外补充截断判断。


内容的提问来源于stack exchange,提问作者Met El Idrissi

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.11.14 18:27:03