PHP无重叠可用时间段检测功能异常,请求排查
PHP时间段重叠检测失效:已存在的预约未被识别
问题场景
我编写了代码用于查找指定日期、员工的无重叠可用时间段,逻辑上应检测到重叠预约,但实际运行出现错误:当检测11:00-12:00的时间段时,数据库中明确存在10:30-11:30的预约,程序却返回count=0,未识别到重叠。
相关代码
主检测函数
function detect_not_overlap($date, $worker_id, $duration) { $available_slots = []; $start_time = strtotime('09:00:00'); $end_time = strtotime('17:00:00'); while ($start_time <= $end_time - $duration * 3600) { $start = date('H:i:s', $start_time); $start_timestamp = strtotime($start); $end = date('H:i:s', $start_time + $duration * 3600); $end_timestamp = strtotime($end); $db = new DatabaseManager(); $apDAO = new AppointmentDAO($db); $count_existing_slots = $apDAO->getOverlappedCount($start_timestamp,$end_timestamp, $date, $worker_id); if ($count_existing_slots == 0 || $count_existing_slots == null) { $available_slots[] = date('H:i:s', $start_time); } $start_time += 30 * 60; //$start_time = strtotime('+30 minutes', $start_time); } return $available_slots; }
数据库查询函数
public function getOverlappedCount($start_time, $end_time, $date, $worker_id) { try { $stmt = $this->db->getConnection()->prepare("SELECT count(*) FROM appointment WHERE end_time > :start_time AND start_time < :end_time AND date = :date AND worker_id = :worker_id"); $stmt->bindValue(":start_time", $start_time); $stmt->bindValue(":end_time", $end_time); $stmt->bindValue(":date", $date); $stmt->bindValue(":worker_id", $worker_id); $stmt->execute(); $count = $stmt->fetchColumn(); return $count; } catch(PDOException $e) { return false; } finally { $this->db->closeConnection(); } }
问题根源排查
1. 时间戳生成错误(核心原因)
主函数中$start_timestamp = strtotime($start);的逻辑存在致命问题:
$start是仅含时分秒的字符串(如11:00:00),strtotime解析时会默认使用当前系统日期生成时间戳,而非传入的$date参数。- 数据库中存储的预约时间是对应
$date的(如2024-05-20 10:30:00),而程序传入查询的时间戳是当前日期的11:00,两者不属于同一天,自然无法匹配到重叠记录。
2. 数据库连接重复创建(性能问题)
每次循环都实例化DatabaseManager和AppointmentDAO并关闭连接,会导致大量不必要的资源开销,虽然不是当前问题的直接原因,但必须优化。
修复方案
方案1:修正时间戳生成逻辑
将传入的$date与时间字符串拼接后生成正确的时间戳,确保与数据库中的日期一致:
function detect_not_overlap($date, $worker_id, $duration) { $available_slots = []; $start_time = strtotime('09:00:00'); $end_time = strtotime('17:00:00'); // 提前初始化数据库连接(优化性能) $db = new DatabaseManager(); $apDAO = new AppointmentDAO($db); // 生成目标日期的0点时间戳,用于后续计算 $target_date_timestamp = strtotime($date . ' 00:00:00'); while ($start_time <= $end_time - $duration * 3600) { $start = date('H:i:s', $start_time); // 生成带目标日期的完整时间戳 $start_timestamp = $target_date_timestamp + ($start_time - strtotime('00:00:00')); $end = date('H:i:s', $start_time + $duration * 3600); $end_timestamp = $target_date_timestamp + ($start_time + $duration * 3600 - strtotime('00:00:00')); $count_existing_slots = $apDAO->getOverlappedCount($start_timestamp,$end_timestamp, $date, $worker_id); if ($count_existing_slots == 0 || $count_existing_slots == null) { $available_slots[] = $start; } $start_time += 30 * 60; } // 循环结束后统一关闭连接 $db->closeConnection(); return $available_slots; }
方案2:确认数据库字段类型匹配
- 如果数据库中
start_time和end_time存储的是完整datetime字符串,需确保传入的时间戳转换为对应格式后再绑定(或直接使用datetime字符串拼接)。 - 如果存储的是当天的秒数(如10:30对应
37800),则需将计算后的完整时间戳转换为当天秒数:$start_seconds = $start_timestamp - $target_date_timestamp;,再传入查询。
可选:优化SQL边界条件
若需要严格禁止首尾相接的预约(如11:30结束的预约,11:30不能开始新预约),可将SQL条件修改为:
SELECT count(*) FROM appointment WHERE end_time >= :start_time AND start_time <= :end_time AND date = :date AND worker_id = :worker_id
内容的提问来源于stack exchange,提问作者Galaxylokka
相关产品推荐
相关产品推荐

