PHP:如何基于DateTime时间间隔创建嵌套分组数组?
嘿,这个分组需求我之前做PHP项目时也碰到过,其实核心逻辑并不复杂——就是在遍历的时候盯着当前组的最后一个时间,和下一个时间比一比,要是间隔超过1天就新开一个组。下面给你拆解思路+具体代码:
核心思路
- 第一步必须确保你的DateTime数组是按时间升序排序的,不然分组逻辑会完全混乱(如果原数组无序,后面会给你补排序代码)
- 初始化两个容器:一个存最终分组结果的大数组,一个存当前正在构建的子组
- 逐个遍历DateTime对象:
- 如果当前子组是空的,直接把第一个时间加进去
- 否则,拿当前时间和子组里最后一个时间算间隔,判断是否超过1天
- 超过1天就把当前子组存入大数组,然后新建子组放当前时间;没超过就直接加入当前子组
- 循环结束后,别忘了把最后一个子组也塞进大数组(不然会丢数据)
完整代码实现
// 示例DateTime数组,先假设已经按时间排序 $dateTimes = [ new DateTime('2024-05-01 10:00:00'), new DateTime('2024-05-01 14:00:00'), new DateTime('2024-05-03 09:00:00'), new DateTime('2024-05-03 15:00:00'), new DateTime('2024-05-05 11:00:00'), ]; // 如果原数组无序,先执行这段排序代码 usort($dateTimes, function($a, $b) { return $a->getTimestamp() - $b->getTimestamp(); }); // 初始化分组容器 $groups = []; $currentGroup = []; foreach ($dateTimes as $dt) { if (empty($currentGroup)) { $currentGroup[] = $dt; continue; } // 取当前组最后一个时间 $lastDt = end($currentGroup); // 两种判断间隔的方式,根据你的业务需求选 // 方式1:严格按24小时计算(时间差>86400秒) $isOverOneDay = ($dt->getTimestamp() - $lastDt->getTimestamp()) > 86400; // 方式2:按自然日天数差计算(比如5-1到5-3间隔2天,超过1天) // $interval = $lastDt->diff($dt); // $isOverOneDay = $interval->days > 1; if ($isOverOneDay) { // 存入当前组,新建子组 $groups[] = $currentGroup; $currentGroup = [$dt]; } else { $currentGroup[] = $dt; } } // 把最后一个组加入结果 if (!empty($currentGroup)) { $groups[] = $currentGroup; } // 测试输出分组结果 foreach ($groups as $index => $group) { echo "组 " . ($index + 1) . ":\n"; foreach ($group as $dt) { echo "- " . $dt->format('Y-m-d H:i:s') . "\n"; } echo "\n"; }
关键细节说明
- 排序的必要性:如果你的DateTime数组是随机顺序的,一定要用
usort先排序,否则分组会完全不符合预期 - 间隔判断的两种场景:
- 如果你需要严格的"24小时间隔"(比如昨天10点到今天11点算超过1天,昨天23点到今天1点不算),就用时间戳差值的方式
- 如果你按自然日算(比如5月1日和5月3日就算间隔超过1天,不管具体时间),就用
diff()得到的days属性判断
- 最后一组的处理:循环结束后,
currentGroup里大概率还有未存入的元素,必须手动加入groups,不然会丢失最后一组数据
扩展:关联数据求平均值
如果每个DateTime还绑定了需要计算平均值的数据,可以把结构改成数组嵌套,后续计算就很方便:
// 示例带数据的结构 $dateData = [ ['dt' => new DateTime('2024-05-01 10:00:00'), 'value' => 10], ['dt' => new DateTime('2024-05-01 14:00:00'), 'value' => 20], ['dt' => new DateTime('2024-05-03 09:00:00'), 'value' => 30], ['dt' => new DateTime('2024-05-03 15:00:00'), 'value' => 40], ['dt' => new DateTime('2024-05-05 11:00:00'), 'value' => 50], ]; // 排序(按时间) usort($dateData, function($a, $b) { return $a['dt']->getTimestamp() - $b['dt']->getTimestamp(); }); // 分组逻辑和之前类似,只是操作的是$item['dt'] $groups = []; $currentGroup = []; foreach ($dateData as $item) { if (empty($currentGroup)) { $currentGroup[] = $item; continue; } $lastItem = end($currentGroup); $isOverOneDay = ($item['dt']->getTimestamp() - $lastItem['dt']->getTimestamp()) > 86400; if ($isOverOneDay) { $groups[] = $currentGroup; $currentGroup = [$item]; } else { $currentGroup[] = $item; } } if (!empty($currentGroup)) $groups[] = $currentGroup; // 计算每组平均值 foreach ($groups as $index => $group) { $total = array_reduce($group, function($sum, $item) { return $sum + $item['value']; }, 0); $avg = $total / count($group); echo "组 " . ($index + 1) . " 的平均值:" . number_format($avg, 2) . "\n"; }
内容的提问来源于stack exchange,提问作者WA Martin
相关产品推荐
相关产品推荐

