PHP脚本无法正确识别多预订时段及非整点预订的可用时段问题
问题:查询指定时间范围内排除已预订时段后的可用固定时长时段
原PHP脚本存在以下问题:
- 仅能正确处理第一个已预订时段,无法识别后续的预订时段
- 无法检测非整点预订时段(如12:10)与可用时段的冲突
示例场景:
时间范围为9:00-15:00,已预订时段为11:10-12:10、14:00-15:00,预期返回可用60分钟时段:9:00、10:00、12:10
原脚本代码
$start_time = '2022-10-21 09:00:00'; //开始时间字符串 $end_time = '2022-10-21 15:00:00'; //结束时间字符串 $booked = ['2022-10-21 12:00:00','2022-10-21 13:00:00']; //已预订时段数组(两两配对) $start = DateTime::createFromFormat('Y-m-d H:i:s',$start_time); //创建DateTime对象 $end = DateTime::createFromFormat('Y-m-d H:i:s',$end_time); //创建DateTime对象 $time1 = $start; $count = 0; //可用时段数量 $out = array(); //可用时段数组 for($i = $start; $i<$end;) //循环遍历时间范围 { $avoid = false; $t1 = date_timestamp_get($i); $t2 = $t1+(60*60); for($k=0;$k<sizeof($booked);$k+=2) //检查已预订时段 { $st = DateTime::createFromFormat('Y-m-d H:i:s',$booked[$k]); $en = DateTime::createFromFormat('Y-m-d H:i:s',$booked[$k+1]); if( $t1 >= date_timestamp_get($st) && $t2 <= date_timestamp_get($en) ) $avoid = true; //标记为已预订 } $slots =[ $i->format('H:i'),$i->modify("+60 minutes")->format('H:i')]; if(!$avoid && $i<$end) //若未预订且在时间范围内 { $count++; array_push($out,$slots); //添加到可用时段数组 } } var_dump($out); //输出结果
解决方案
1. 合并重叠/连续的预订时段
使用mergeBookings函数将重叠或连续的预订时段合并,避免重复检查,简化冲突判断逻辑:
function mergeBookings($ranges) { $retVal = []; // 按开始时间对预订时段排序 usort($ranges, function ($a, $b) { return strcmp($a['start'], $b['start']); }); $currentRange = []; foreach ($ranges as $range) { // 跳过无效的时段(开始时间晚于等于结束时间) if ($range['start'] >= $range['end']) { continue; } // 初始化第一个时段 if (empty($currentRange)) { $currentRange = $range; continue; } // 当前时段与上一个时段不重叠,保存上一个时段并切换到当前时段 if ($currentRange['end'] < $range['start']) { $retVal[] = $currentRange; $currentRange = $range; } // 当前时段与上一个时段重叠,合并为一个更长的时段 elseif ($currentRange['end'] < $range['end']) { $currentRange['end'] = $range['end']; } } // 保存最后一个时段 if ($currentRange) { $retVal[] = $currentRange; } return $retVal; }
2. 修正主逻辑脚本
修改主脚本解决冲突检测问题,同时支持自定义时段时长:
$start_time = '2022-10-21 09:00:00'; $end_time = '2022-10-21 15:00:00'; // 改用结构化数组存储预订时段,每个元素包含start和end $booked = [ ['start' => '2022-10-21 11:10:00', 'end' => '2022-10-21 12:10:00'], ['start' => '2022-10-21 14:00:00', 'end' => '2022-10-21 15:00:00'] ]; $slotDuration = 60; // 自定义时段时长(分钟) $start = DateTime::createFromFormat('Y-m-d H:i:s', $start_time); $end = DateTime::createFromFormat('Y-m-d H:i:s', $end_time); $availableSlots = []; // 合并重叠预订时段 $mergedBooked = mergeBookings($booked); // 遍历生成所有可能的时段 $currentSlotStart = clone $start; while ($currentSlotStart->getTimestamp() + ($slotDuration * 60) <= $end->getTimestamp()) { $currentSlotEndTimestamp = $currentSlotStart->getTimestamp() + ($slotDuration * 60); $isAvailable = true; // 检查当前时段是否与任何预订时段冲突 foreach ($mergedBooked as $booking) { $bookingStart = DateTime::createFromFormat('Y-m-d H:i:s', $booking['start'])->getTimestamp(); $bookingEnd = DateTime::createFromFormat('Y-m-d H:i:s', $booking['end'])->getTimestamp(); // 冲突判断:只要当前时段与预订时段有重叠,就标记为不可用 if (!(($currentSlotEndTimestamp <= $bookingStart) || ($currentSlotStart->getTimestamp() >= $bookingEnd))) { $isAvailable = false; break; } } if ($isAvailable) { $availableSlots[] = $currentSlotStart->format('H:i'); } // 推进到下一个时段 $currentSlotStart->modify("+{$slotDuration} minutes"); } var_dump($availableSlots);
关键改动说明
- 预订时段改用结构化数组,避免原一维数组两两配对的歧义
- 添加
mergeBookings函数合并重叠时段,优化检查效率 - 修正冲突检测逻辑:不再要求可用时段完全包含在预订时段内,只要有重叠就排除
- 支持自定义时段时长(如设置为45即可生成45分钟间隔的时段)
内容的提问来源于stack exchange,提问作者lStoilov
相关产品推荐
相关产品推荐

