【问题标题】:PHP: How do I check if one UNIX timestamp range overlaps any UNIX timestamp range?PHP:如何检查一个 UNIX 时间戳范围是否与任何 UNIX 时间戳范围重叠?
【发布时间】:2017-02-28 20:14:41
【问题描述】:

如何检查一个 UNIX 时间戳范围是否与 PHP 中的另一个 UNIX 时间戳范围重叠?

我正在开发一个接受未来预订的应用程序。但是,每个期间只允许一 (1) 次预订


示例

先生。 X 从 10:00 A.M.12:00 P.M.(中午)预订资源。稍后,Y 女士想从 8:00 A.M.11:00 P.M. 保留该相同的资源。我的申请应拒绝 Y 女士的预订尝试,因为它 X 先生的先前预订重叠。


我将现有预订的开始和结束时间存储在 UNIX 时间戳(整数)中,但如果需要,我可以将它们转换为以下格式“yyyy-mm-dd hh:mm:ss”,反之亦然。

我不明白如何解决这个问题。如果我用所有现有的预订开始时间检查新的开始时间,并以类似的方式检查新的结束时间,逻辑将有许多if 语句并使应用程序变慢。

请您帮我以一种高效的方式解决这个问题,而不需要使用大量的服务器资源。

非常感谢您的帮助。

谢谢

【问题讨论】:

  • 所以...如果有效的约会应该始终是 start1
  • 感谢@Gerrit0 的回答,我在发布问题后想到了以下if 语句。如果 StartA > EndB 则为真 如果 EndA
  • 可能并不明显,但所有这些条件都可以简化为一个:SELECT ... WHERE start < :end AND end > :start(如果找到行 - 重叠)。有关详细信息,请参阅here
  • @shudder 啊,但是冲突的原因就不知道了。
  • @Gerrit0 有点像我的回答。

标签: php date datetime time unix-timestamp


【解决方案1】:

简介

换句话说,您需要对特定资源的所有预留间隔(UNIX 时间戳)进行比较,以确定新预留是否有效(在域内 用于新预订)。

步骤 1

首先,类似的 SQL 查询可能会有所帮助。虽然ANYALLNOTEXISTS 等关键词看起来很诱人,但在发生调度冲突时需要多少信息(根据您的 UI)由您决定.此查询提供了提取关于潜在预订的最大信息量(在 PHP 等中)的机会前瞻预测。

// A query like this might help. It's not perfect, but you get the idea.
// This query looks for ALL potential conflicts, starting and ending.

    $sql = "SELECT DISTINCT `t1`.`startTime`, `t1`.`endTime`
              FROM `reservations` AS `t1`
             INNER JOIN `resources` AS `t2`
                ON `t1`.`resourceId` = `t2`.`resourceId`
             WHERE `t2`.`resourceId` = :resourceId
               AND (`t1`.`startTime` BETWEEN :minTime1 AND :maxTime1)
                OR (`t1`.`endTime` BETWEEN :minTime2 AND :maxTime2)
             ORDER BY `t1`.`startTime` ASC";

可能。这将为您留下一个多维数组。以下逻辑允许您获得一份报告,详细说明为什么无法进行预订。您可以在另一个模块中解释报告。

第二步

将解决方案概括为Reservation 类的方法。根据您的 RDBMS,您可能能够在 SQL 中执行类似的操作。虽然,它可能会远没有那么具体,您以后可能需要粒度。您可以将 JSON 格式的报告发送到 JavaScript 前端(只是需要考虑的事情)。

private function inOpenDomain(array $exclusion, $testStart, $testEnd)
{
    $result = null;
    $code   = null;

    $start = $exclusion[0];
    $end   = $exclusion[1];

    if (($testStart > $end) || ($testEnd < $start)) {
        $result = true;
        $code = 0;  //Good! No conflict.
    } elseif ($testStart === $start) {
        $result = false;
        $code = 1;
    } elseif ($testStart === $end) {
        $result = false;
        $code = 2;
    } elseif ($testEnd === $start) {
        $result = false;
        $code = 3;
    } elseif ($testEnd === $end) {
        $result = false;
        $code = 4;
    } elseif (($testStart > $start) && ($testEnd < $end)) {   //Middle
        $result = false;
        $code = 5;
    } elseif (($testStart < $start) && ($testEnd > $start)) { //Left limit
        $result = false;
        $code = 6;
    } elseif (($testStart < $end) && ($testEnd > $end)) {     //Right limit
        $result = false;
        $code = 7;
    } elseif (($testStart < $start) && ($testEnd > $end)) {   //Both limits
        $result = false;
        $code = 8;
    } else {
        $result = false;
        $code = 9;
    }

    return ['start' => $start, 'end' => $end, 'result' => $result => 'code' => $code];
}

第三步

创建一个方法来管理对先前预订时间的检查(假设为PDO::FETCH_ASSOC)。

private function checkPeriods(array $periods, $newStartTime, $newEndTime)
{
    $report = [];

    if (!isset($periods[0])) { //If NOT multi-dimensional
        $report = inOpenDomain($periods, $newStartTime, $newEndTime)
    } else {
        for ($i = 0, $length = $count($periods); $i < $length; ++$i) {
            $report[$i] = inOpenDomain($periods[$i], $newStartTime, $newEndTime);
        }
    }

    return $report;
}

第四步

使用 PDO 准备好的语句在 reservations 表上执行 SELECT 的方法。一般来说,...

private function getReservationTimes($resourceId, $minTime, $maxTime) 
{
    $sql = "SELECT DISTINCT `t1`.`startTime`, `t1`.`endTime`
              FROM `reservations` AS `t1`
             INNER JOIN `resources` AS `t2`
                ON `t1`.`resourceId` = `t2`.`resourceId`
             WHERE `t2`.`resourceId` = :resourceId
               AND (`t1`.`startTime` BETWEEN :minTime1 AND :maxTime1)
                OR (`t1`.`endTime` BETWEEN :minTime2 AND :maxTime2)
             ORDER BY `t1`.`startTime` ASC";

    $stmt = $this->db->prepare($sql);
    $stmt->bindParam(:resourceId , $resourceId);
    $stmt->bindParam(:minTime1 , $minTime);
    $stmt->bindParam(:maxTime1 , $maxTime);
    $stmt->bindParam(:minTime2 , $minTime);
    $stmt->bindParam(:maxTime2 , $maxTime);
    $stmt->execute();
    return $stmt->fetchAll();
}

第 5 步

为整个过程创建一个公共方法(接口)。

public function isOpen($minTime, $maxTime)
{
    $periods = $this->getReservationTimes($this->resource->getResourceId(), $minTime, $maxTime);

    if (empty($periods)) {
        return true;  //You may reserve the resource during the time period.
    }

    return $this->checkPeriods($periods, $this->start, $this->end));  
}

第 6 步

分离关注点。

为实际保留的项目创建一个类层次结构。

abstact class Product
{

}

class Resource extends Product implements Reservable  //Or, something ...
{
    private $resourceId;

   //etc ....
}

为预订创建一个类层次结构。

abstract class Interval
{
    private $start;
    private $end;

    public function __construct($start, $end)
    {
        $this->start = $start;
        $this->end   = $end;
    }
}

class Reservation extends Interval
{
    private $db;
    private $resource;

    public function __construct(PDO $pdo, Reservable $resource, $reqStartTime, $reqEndTime)
    {
        parent::__construct($reqStartTime, $reqEndTime);
        $this->db = $pdo;
        $this->resource = $resource;
    }
}

第 7 步

在 try/catch 中运行

当您实例化 Reservation 对象时,至少提供一个 Reservable 对象、请求的开始时间和请求的结束时间(在本例中为 UNIX 时间戳)。

try
{
    $day          = 84600;                   // Seconds per day.
    $future       = $day * 90;               // Application specific.

    //User requested times.
    $reqStartTime = 1488394687 + $day;       // Tomorrow.
    $reqEndTime   = 1488394687 + ($day * 2); // Two day duration.

    //Search constraints.
    $minTime      = time();                   // Today. Right now.
    $maxTime      = 1488394687 + $future;     // 90 day look ahead.

    $reservation  = new Reservation($pdo, $resourceObj, $reqStartTime, $reqEndTime);
    $availability = $reservation->isOpen($minTime, $maxTime);

    if($availability === true){
        $reservation->confirm();
    } else {
        //Have some other object deal with the report
        $reporter = new Reporter($availability);
        $reporter->analyzeReport();
        //Have some other object update the view, etc ...
    }

}
catch(Exception $e)
{
    //Handle it.
}

【讨论】:

  • 这只会检查开始时间,如果它在数组中。如果它在数组中,它将停止执行并说您不能保留此资源。就像我给出的示例一样,开始时间是上午 8 点,预留资源开始时间是上午 10 点。基于使用 in_array() 的函数,它会给你 false,因为 8 AM 在保留的时间戳中不存在,它会让我保留它不应该的资源,因为 8 AM 到 11 AM 的日期范围确实与现有的保留重叠.结束时间检查也是如此。
  • 好的,现在,怎么样。
  • 好吧,这个怎么样。
  • 我为您创建了一个检查预订的功能。它可能会起作用。
  • 没有通过下面if语句中的测试用例
     startTime= 2017-2-28 14:00 endTime= 2017-2-28 16:00 newStartTime= 2017 -2-28 12:00 newEndTime= 2017-2-28 13:00 if (($newStartTime 
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-10-09
  • 2019-03-19
  • 1970-01-01
  • 2016-06-30
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多