多维数组与foreach循环下,取首尾索引计算员工项目每日工时
解决方案:按项目维度计算员工每日工作时长
嘿,看起来你已经把员工任务分配功能的大部分开发搞定了,现在就差按项目维度计算每日工时这一步了。我刚好有个贴合你需求的PHP实现方案,完全利用多维数组的首尾索引来判断项目的首日、末日和中间工作日,严格遵循你给定的计算规则:
核心思路
- 先定义办公时间常量,转成时间戳方便计算时长差值
- 逐层遍历你的三维数组(员工ID→项目ID→日期)
- 对每个项目,获取日期列表的首尾索引,以此区分首日、末日、中间工作日
- 针对不同场景计算工时,最后替换原数组中的起止时间为计算结果
PHP代码实现
<?php // 设置时区(根据业务需求调整,比如国内用Asia/Shanghai) date_default_timezone_set('Asia/Shanghai'); // 定义办公时间常量,转换为时间戳简化计算 define('WORK_START', strtotime('09:30:00')); define('WORK_END', strtotime('19:30:00')); define('STANDARD_DAILY_HOURS', 10); // 每日标准工时 // 模拟你的现有三维数组结构(员工ID→项目ID→日期→起止时间) $employeeTaskData = [ 'emp_001' => [ 'proj_001' => [ '2024-05-20' => ['t_start_time' => '2024-05-20 10:15:00', 't_end_time' => '2024-05-20 19:30:00'], '2024-05-21' => ['t_start_time' => '2024-05-21 09:30:00', 't_end_time' => '2024-05-21 19:30:00'], '2024-05-22' => ['t_start_time' => '2024-05-22 09:30:00', 't_end_time' => '2024-05-22 15:45:00'] ], 'proj_002' => [ '2024-05-23' => ['t_start_time' => '2024-05-23 13:30:00', 't_end_time' => '2024-05-23 19:30:00'] ] ], 'emp_002' => [ 'proj_003' => [ '2024-05-20' => ['t_start_time' => '2024-05-20 09:30:00', 't_end_time' => '2024-05-20 19:30:00'], '2024-05-21' => ['t_start_time' => '2024-05-21 09:30:00', 't_end_time' => '2024-05-21 19:30:00'], '2024-05-22' => ['t_start_time' => '2024-05-22 09:30:00', 't_end_time' => '2024-05-22 12:10:00'] ] ] ]; // 处理数组,计算并替换每日工时 foreach ($employeeTaskData as $empId => &$projects) { foreach ($projects as $projId => &$dateTasks) { // 获取当前项目的所有日期索引,用于判断首日和末日 $dateKeys = array_keys($dateTasks); $firstDate = reset($dateKeys); $lastDate = end($dateKeys); foreach ($dateTasks as $date => &$task) { $startTs = strtotime($task['t_start_time']); $endTs = strtotime($task['t_end_time']); $dailyHours = 0; if ($date === $firstDate && $date === $lastDate) { // 场景1:项目仅持续1天,取办公时间内的有效时长 $effectiveStart = max($startTs, WORK_START); $effectiveEnd = min($endTs, WORK_END); $dailyHours = round(($effectiveEnd - $effectiveStart) / 3600, 2); } elseif ($date === $firstDate) { // 场景2:项目首日,计算任务开始到下班的时长 $effectiveStart = max($startTs, WORK_START); $dailyHours = round((WORK_END - $effectiveStart) / 3600, 2); } elseif ($date === $lastDate) { // 场景3:项目末日,计算上班到任务结束的时长 $effectiveEnd = min($endTs, WORK_END); $dailyHours = round(($effectiveEnd - WORK_START) / 3600, 2); } else { // 场景4:中间工作日,直接按标准10小时计算 $dailyHours = STANDARD_DAILY_HOURS; } // 替换原起止时间为每日工时(可根据需求调整结构,比如保留原字段+新增工时) $task = ['daily_hours' => $dailyHours]; } } } // 打印结果(实际业务中可按需存储或返回) echo "<pre>"; print_r($employeeTaskData); echo "</pre>"; ?>
关键细节说明
- 时区处理:开头设置了时区,避免因服务器时区差异导致时间计算错误
- 边界值处理:比如首日任务开始时间早于上班时间时,自动从上班时间开始计算;末日任务结束时间晚于下班时间时,自动截止到下班时间
- 精度控制:工时保留两位小数,更符合实际办公场景的统计需求
- 灵活调整:如果需要保留原起止时间字段,只需修改最后替换的逻辑,比如改成
$task['daily_hours'] = $dailyHours;即可
额外建议
如果需要处理非工作日(比如周末),可以添加判断:用date('N', strtotime($date))获取日期对应的星期数(1=周一,5=周五),非工作日的工时可设为0或跳过计算。
内容的提问来源于stack exchange,提问作者Mithun M
相关产品推荐
相关产品推荐

