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

Laravel6结合Carbon实现跨日期夜班的当日考勤记录查询

问题场景
  • 业务使用attendance表存储考勤记录,表中start_at、end_at字段为datetime类型,所有时间以UTC格式存储
  • 项目使用php-carbon处理时间逻辑,约束条件:不允许修改表设计,不允许更改两个时间字段的类型
  • 需要实现当日考勤记录筛选功能,必须兼容用户时区转换(前端按用户所在时区展示时间,全流程通过Carbon处理时区逻辑)
现存Bug

原有逻辑仅匹配start_at日期和当前日期一致的记录,无法适配跨天夜班场景:

示例:用户夜班开始时间为2022-07-08 23:00 UTC,结束时间为2022-07-09 09:00 UTC,7月9日用户查询当日考勤时,原有逻辑用carbon::today()/carbon::now()直接匹配start_at日期,会因为start_at日期是7月8日导致查询不到该条记录。

原有问题代码:

public function today_Attendance($user_id, $shift_id)
{
    $todays_attendance = \App\EmployeeWorkSchedule::where('work_schedule_time_id', $shift_id)
               ->where('user_id', $user_id)
               ->whereDate('start_at', carbon::now())
               ->first();
    return $todays_attendance;
}
修复方案

核心查询逻辑覆盖两类属于当日的考勤记录:

  1. 用户时区下当日0点之后开始的考勤(常规白班/当日开始的班次)
  2. 用户时区下前一天开始、但结束时间晚于当日0点的考勤(跨天夜班)

所有时间边界先按用户所在时区计算,再转换为UTC格式和数据库存储值匹配,避免时区偏移导致的日期计算错误。

修复后代码:

public function today_Attendance($user_id, $shift_id)
{
    // 替换为项目中实际获取用户时区的逻辑
    $userTimezone = auth()->user()->timezone;
    // 计算用户时区下当日的UTC起止时间
    $todayStartInUserTz = Carbon::now($userTimezone)->startOfDay();
    $todayStartUtc = $todayStartInUserTz->copy()->setTimezone('UTC');
    $todayEndUtc = $todayStartInUserTz->copy()->endOfDay()->setTimezone('UTC');
    // 计算跨天班次的查询起始边界(常规夜班时长不会超过24小时,取前一天0点即可覆盖)
    $yesterdayStartUtc = $todayStartInUserTz->copy()->subDay()->startOfDay()->setTimezone('UTC');

    $todays_attendance = \App\EmployeeWorkSchedule::where('work_schedule_time_id', $shift_id)
        ->where('user_id', $user_id)
        ->where(function ($query) use ($todayStartUtc, $todayEndUtc, $yesterdayStartUtc) {
            // 匹配当日开始的班次
            $query->whereBetween('start_at', [$todayStartUtc, $todayEndUtc])
                // 匹配前一天开始、跨天到当日的夜班
                ->orWhere(function ($subQuery) use ($todayStartUtc, $yesterdayStartUtc) {
                    $subQuery->whereBetween('start_at', [$yesterdayStartUtc, $todayStartUtc])
                        ->where('end_at', '>=', $todayStartUtc);
                });
        })
        ->first();
    
    return $todays_attendance;
}
注意事项
  • 禁止直接使用数据库层面的whereDate做日期匹配,该方法会按数据库存储的UTC时间计算日期,和用户所在时区的自然日存在时差,会导致查询结果偏移
  • 跨天查询边界取前一天0点作为起点,可覆盖所有常规时长的夜班场景,如果业务存在连续超过24小时的班次,可根据实际业务调整往前追溯的时间范围

内容的提问来源于stack exchange,提问作者Haider Ali Khan

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.26 23:27:35