【问题标题】:Intersection of Days in PHP ArrayPHP数组中的天数的交集
【发布时间】:2021-01-14 15:23:44
【问题描述】:

对于预订系统,我想查看用户输入的开始和结束日期是否与存储在两个数组中的开始和结束日期相交。一个数组保存所有开始日期,一个数组保存所有结束日期。数组中的值之前已经从数据库中获取过。

$startArray = [0 => "1.1.2021", 1 => "5.1.2021", 2 => "10.1.2021"]; 
$endArray = [0 => "6.1.2021", 1 => "8.1.2021", 2 => "13.1.2021"];
$startUser = "3.1.2021";
$endUser = "5.1.2021";

我的目标是查看用户所做的选择是否与数组中的选择相交。 这个我试过了

foreach ($startArray as $key => $value) {
            if (($startUser <= $endArray[$key]) && ($endUser >= $value)) {
                //action;
            }

但这不起作用。我最好怎么做?

编辑:日期格式实际上是 2021-01-01。既在数组中,又在用户输入中。

【问题讨论】:

  • 我有点不清楚。 “开始日期”和“结束日期”是固定的吗?意思是,如果我从 1.1.2021 开始,我必须在 6.1.2021 或更早之前完成(两者都是数组中的第一个分支)
  • 交叉点意味着任何形式的重叠。从逻辑的角度来看,这意味着用户开始在 db end 之前,而 user end 在 db start 之后。这就是你的目标吗?
  • 另外,如果您将间隔组织成一个数组,其中每个元素都是一个包含开始日期和结束日期的数组,这可能会使您的比较更容易。
  • @El_Vanja 不完全是。该数据库仅包含预订系统的所有预订列表。数组中的开始日期和结束日期按索引属于一起。 startUser 和 enduser 是用户输入的值,他们希望将预订添加到数据库中。我只想检查用户给出的日期是否与数据库中另一个已经存在的预订冲突。我只是使用数组来存储所有结果。
  • @EdenMoshe 数组中的值相互关联。 startArray 中的索引 0 是开始,endArray 中的索引 0 是该持续时间的结束。与索引 1、2 等相同。这些日期都是固定的,不能更改,因此如果它从 1.1.2021 开始,它必须在 6.1.2021 结束。我想知道 startUser 和 endUser 的持续时间是否与数组中的任何时间跨度重叠。

标签: php date date-comparison


【解决方案1】:

很难比较这样的字符串。检查日期时我喜欢做的是使用可以更容易比较的 DateTime 对象。

$d1 = new DateTime('2008-08-03 14:52:10');
$d2 = new DateTime('2008-01-03 11:11:10');
var_dump($d1 == $d2);
var_dump($d1 > $d2);
var_dump($d1 < $d2);
?>
bool(false)
bool(true)
bool(false)

【讨论】:

  • 如何使用它来检查 3.1.2021 到 5.1.2021 的时间跨度是否与另一个时间跨度重叠?
【解决方案2】:

您的代码的问题在于您没有检查每个日期是否都在两个日期之间。 开始日期应大于开始日期并小于结束日期。 结束日期应大于开始日期且小于结束日期

<?php
$startArray = [0 => "1.1.2021", 1 => "5.1.2021", 2 => "10.1.2021"];
$endArray = [0 => "6.1.2021", 1 => "8.1.2021", 2 => "13.1.2021"];
$startUser = DateTime::createFromFormat('d.m.Y', "3.1.2021");
$endUser =  DateTime::createFromFormat('d.m.Y', "5.1.2021");

if ($startUser->diff($endUser)->format("%R%d") <= 0){
    return false;
}
foreach ($startArray as $key => $value) {
    $start = DateTime::createFromFormat('d.m.Y', $value);
    $end = DateTime::createFromFormat('d.m.Y', $endArray[$key]);
    if ($startUser->diff($start)->format("%R%d") <= 0 && $startUser->diff($end)->format("%R%d") >=0 && $endUser->diff($start)->format("%R%d") <= 0 && $endUser->diff($end)->format("%R%d") >=0 ) {
        return true;
    }
}
return false;

为了秩序和维护的方便,我会稍微改变一下数据的结构。 我会建立这样一个数组

$dates = [['start' => "1.1.2021", 'end'= >"6.1.2021"] , ['start' => "5.1.2021", 'end'= >"8.1.2021"]

【讨论】:

  • 此日期格式不可靠,无法进行比较。以var_dump("5.2.2021" &lt;= "6.1.2021") 为例。
  • 已修复。谢谢。
  • 不过,您可以大大简化该条件。见general date range overlap logicif ($startUser &lt;= $end &amp;&amp; $endUser &gt;= $start) 应该完全覆盖它。
  • 不仅过于复杂,而且是错误的。请参阅我修改日期的fiddle。我将它们设置为与数组中的最后一个间隔重叠,但它没有输入if
  • 23.1.2021 &lt; 25.1.2021
【解决方案3】:

您的问题可以通过单个查询在数据库级别解决。让我们假设以下简化的数据库设置:

  • 房间桌子
number price_class
1 Economy
2 Economy
3 Luxury
4 Luxury
  • bookings
room_number start_date end_date
1 2021-06-11 2021-06-15
2 2021-06-11 2021-06-15
3 2021-06-11 2021-06-15

现在我们还假设用户搜索术语2021-06-09 - 2021-06-12。这意味着该期间唯一未占用的房间是房间 4。如果我们搜索经济房间,应该没有结果,如果我们搜索豪华房间,结果中应该只有房间 4。以下查询执行此操作:

SELECT number FROM rooms
WHERE price_class = :priceClass AND number NOT IN (
  SELECT DISTINCT room_number FROM bookings b
  INNER JOIN rooms r ON r.number = b.room_number AND r.price_class = :priceClass
  WHERE start_date <= :userEndDate AND end_date >= :userStartDate
)

您可以在this fiddle在线测试。

它是如何工作的:

  • 子查询选择给定价格等级的所有日期重叠的房间。重叠逻辑在this post 中有很好的解释。我们使用 distinct 是因为一个重叠就足够了(理论上,用户输入可以在同一个房间内与多个间隔重叠,但这没关系 - 只要一个重叠,我们就知道它不可用)。
  • 然后,主查询选择在重叠查询中未找到编号的给定价格等级的所有房间(意味着它们可用)。

这将返回所有可用的房间。如果您只需要 n 个房间,只需在主查询中添加 LIMIT n。如果要返回随机可用房间,可以在主查询中添加ORDER BY RAND()

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2021-03-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-04-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多