You need to enable JavaScript to run this app. PHP 转换日期字符串适配MySQL日期范围比对的实现方法
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.31 19:54:04