PHP/Laravel预约系统:剔除特殊时段生成可预约时间槽方法
PHP/Laravel 预约系统可用时段拆分实现
核心需求
从给定的基础工作时段中,剔除指定的不可预约/已占用时段,拆分得到所有连续的可用时间区间,示例输入输出如下:
- 基础工作时段
08:00 to 13:00 16:00 to 21:00
- 待剔除不可预约时段:
09:30 to 11:00、17:00 to 18:00 - 预期输出可用时段
08:00 to 09:30 11:00 to 13:00 16:00 to 17:00 18:00 to 21:00
初始数据结构:
$maintimes = [ ['start' => '08:00', 'end' => '13:00'], ['start' => '16:00', 'end' => '21:00'], ]; // 待剔除的不可预约时段 $removes = [ ['start' => '09:30', 'end' => '11:00'], ['start' => '17:00', 'end' => '18:00'] ];
实现逻辑
直接用时间字符串比较容易出现边界判断误差,统一转成当日0点起算的分钟数做数值计算,处理完成后再转回HH:MM格式即可,核心步骤如下:
- 遍历每个基础工作时段,先筛选出所有和当前工作时段有重叠的不可预约时段,按开始时间从小到大排序
- 用指针标记当前可用区间的起点,初始值为工作时段的开始时间
- 逐个遍历重叠的不可预约时段:如果指针位置在不可预约时段起点之前,说明指针到不可预约时段起点之间是可用区间,存入结果;再把指针移动到不可预约时段的终点
- 所有不可预约时段遍历完成后,如果指针位置还在工作时段结束时间之前,把指针到工作时段终点的最后一段区间存入结果
完整实现代码
/** * 从基础工作时段中剔除不可预约时段,返回可用时间槽 * @param array $maintimes 基础工作时段集合,格式:[['start'=>'HH:MM','end'=>'HH:MM'], ...] * @param array $removes 待剔除不可预约时段集合,格式同$maintimes * @return array 可用时间槽集合 */ function getAvailableSlots(array $maintimes, array $removes): array { // HH:MM格式转当日0点起算的分钟数 $toMinutes = function(string $time): int { [$h, $m] = explode(':', $time); return (int)$h * 60 + (int)$m; }; // 分钟数转回HH:MM格式 $toTime = fn(int $min): string => sprintf('%02d:%02d', intdiv($min, 60), $min % 60); $available = []; foreach ($maintimes as $slot) { $slotStart = $toMinutes($slot['start']); $slotEnd = $toMinutes($slot['end']); $cursor = $slotStart; // 筛选和当前工作时段重叠的剔除时段,按开始时间升序排列 $overlaps = array_filter($removes, function($r) use ($slotStart, $slotEnd, $toMinutes) { $rStart = $toMinutes($r['start']); $rEnd = $toMinutes($r['end']); return $rStart < $slotEnd && $rEnd > $slotStart; }); usort($overlaps, fn($a, $b) => $toMinutes($a['start']) <=> $toMinutes($b['start'])); foreach ($overlaps as $block) { $bStart = max($toMinutes($block['start']), $slotStart); $bEnd = min($toMinutes($block['end']), $slotEnd); if ($cursor < $bStart) { $available[] = ['start' => $toTime($cursor), 'end' => $toTime($bStart)]; } $cursor = max($cursor, $bEnd); } // 补全最后一段可用区间 if ($cursor < $slotEnd) { $available[] = ['start' => $toTime($cursor), 'end' => $toTime($slotEnd)]; } } return $available; } // 调用示例 $output = getAvailableSlots($maintimes, $removes);
边界兼容说明
该实现自动覆盖以下特殊场景,不需要额外添加判断逻辑:
- 不可预约时段部分超出工作时段边界(比如不可预约时段是
07:00-09:00,工作时段是08:00-13:00,会自动截断到工作时段范围内计算) - 多个不可预约时段连续/重叠
- 不可预约时段完全覆盖某一段工作时段
- 不可预约时段完全落在工作时段外(会自动过滤不处理)
传入示例数据运行后,$output返回结果和预期完全一致。
内容的提问来源于stack exchange,提问作者Tasnim Eskandari
相关产品推荐
相关产品推荐

