PHP 转换日期字符串适配MySQL日期范围比对的实现方法
派对装备租赁系统日期校验功能问题修复
业务背景
当前开发的派对装备租赁网站核心功能为:用户在表单输入拟租用的起止日期后,系统校验对应装备在目标时段的可租用库存,判断是否满足订单要求。
现存问题
- 日期格式不匹配:PHP端获取的用户输入日期字符串,未做格式标准化处理,无法直接和MySQL中以
YYYY-MM-DD格式存储的日期字段做准确比对 - SQL语法错误:拼接生成的SQL语句中,日期值未添加单引号包裹,导致语句执行失效。例如实际生成的语句会把
2021-03-04解析为算术计算值(2021-3-4=2014),完全无法匹配正确的日期逻辑 - 日期重叠判断逻辑冗余:原有多组OR判断的区间重叠条件存在重复,未覆盖全所有交叉场景
出问题的原始SQL如下:
SELECT SUM(quantity) AS 'TotalRented' from orders, order_details where equip_id =12 AND order_details.order_ID = orders.order_id AND ( ( start_date >=2021-03-04 AND end_date <= 2021-03-12) OR (start_date <= 2021-03-04 AND end_date >= 2021-03-12) OR ( start_date <= 2021-03-12 AND end_date >= 2021-03-12) OR (start_date <= 2021-03-04 AND end_date >= 2021-03-04) );
分步修复方案
1. PHP端日期格式标准化处理
不依赖隐式类型转换,使用PHP原生DateTime类将用户输入的任意合法日期字符串,转换为MySQL要求的Y-m-d格式,同时做日期合法性校验,避免非法值流入数据库逻辑:
// 从表单获取用户输入的原始日期 $rawStart = $_POST['start_date']; $rawEnd = $_POST['end_date']; try { // 根据表单实际的日期输入格式,替换createFromFormat的格式参数 // 例:如果表单输入为月/日/年格式,此处替换为'm/d/Y' $startObj = DateTime::createFromFormat('Y-m-d', $rawStart); $endObj = DateTime::createFromFormat('Y-m-d', $rawEnd); // 校验输入日期的合法性(如排除2月30日、4月31日等无效日期) if (!$startObj || !$endObj) { throw new Exception('输入的日期格式不合法'); } // 转换为MySQL要求的YYYY-MM-DD格式字符串 $start_date = $startObj->format('Y-m-d'); $end_date = $endObj->format('Y-m-d'); } catch (Exception $e) { // 捕获校验异常,向前端返回友好提示 die('日期输入错误:' . $e->getMessage()); }
说明:MySQL的
DATE/DATETIME类型字段,天然支持对YYYY-MM-DD格式的合法字符串做值比对,不需要额外做类型转换。
2. 修正SQL拼接的引号问题
MySQL中的日期、字符串类型值,在SQL语句中必须用单引号包裹,否则会被解析为数字表达式,导致逻辑完全错误。
- 基础修正方式:手动为日期值添加单引号,避免被解析为数字运算
- 更优方案:使用MySQLi预处理语句绑定参数,数据库驱动会自动为字符串类参数添加正确的单引号、处理特殊字符转义,同时避免SQL注入风险
3. 简化区间重叠判断逻辑
两段租赁时间存在重叠的核心判断逻辑仅需一行,无需重复书写多组OR条件:已存在租赁记录的开始日期 <= 用户拟租期的结束日期,且已存在租赁记录的结束日期 >= 用户拟租期的开始日期,即可覆盖所有交叉场景。
基于以上调整的核心代码实现:
foreach ($_SESSION['cart'] as $item) { // 先查询装备的基础库存信息 $equipSql = "SELECT quan_on_hand, equip_description FROM equipment WHERE equip_id = ?"; $equipStmt = $db->prepare($equipSql); $equipStmt->bind_param('i', $item['id']); $equipStmt->execute(); $equipRow = $equipStmt->get_result()->fetch_assoc(); $quantity_available = $equipRow['quan_on_hand']; $equip_description = $equipRow['equip_description']; // 查询目标区间内的已租赁总量 // 用COALESCE处理无租赁记录时SUM返回NULL的问题,默认转为0 $rentSql = " SELECT COALESCE(SUM(order_details.quantity), 0) AS TotalRented FROM order_details INNER JOIN orders ON order_details.order_ID = orders.order_id WHERE order_details.equip_id = ? AND order_details.start_date <= ? AND order_details.end_date >= ? "; $rentStmt = $db->prepare($rentSql); // 绑定参数类型:i对应整数equip_id,s对应两个字符串日期 $rentStmt->bind_param('iss', $item['id'], $end_date, $start_date); $rentStmt->execute(); $rentRow = $rentStmt->get_result()->fetch_assoc(); $total_rented = $rentRow['TotalRented']; // 计算可租余量:在手库存 - 目标区间内已租数量 $available_in_range = $quantity_available - $total_rented; // 判断是否满足用户租用需求 $can_rent = $available_in_range >= $item['quantity']; }
调整完成后生成的可正常执行的SQL语句示例:
SELECT COALESCE(SUM(order_details.quantity), 0) AS TotalRented FROM order_details INNER JOIN orders ON order_details.order_ID = orders.order_id WHERE order_details.equip_id = 12 AND order_details.start_date <= '2021-03-12' AND order_details.end_date >= '2021-03-04';
内容的提问来源于stack exchange,提问作者MonoConman
相关产品推荐
相关产品推荐

