PHP : Select overlapping date time ranges with time range overlapping day

PHP : Select overlapping date time ranges with time range overlapping day

我想知道如何获得与其他日期时间范围重叠的日期时间范围。 特殊之处在于日期范围 可以有 时间范围重叠连续两天。

例如,我有一个定期预订 01/01 和 02/01,从 20:00 开始,第二天在 02:00 结束。

第一天:

$start = '2017-01-01 20:00';
$end =   '2017-01-02 02:00';

第二天:

$start = '2017-01-02 20:00';
$end =   '2017-01-03 02:00';

现在我想知道如何获得与此预订重叠的预订。

注意:所有预订都可以有或没有时间范围重叠天。

例如,从 PHP 的角度来看,我有以下保留意见: 为了便于阅读,日期和时间在这里表示为字符串。实际上它们是 DateTime。

//The reservation
$reservation= array(
   array(
      'day' => '2017-01-01',
      'time_range' => array('20:00', '23:59')
   ),
   array(
      'day' => '2017-01-02',
      'time_range' => array('00:00', '02:00')
   ),
);

//Other reservations
$reservations= array(
   //Reservation doesn't overlap
   array(
      'day' => '2017-01-01',
      'time_range' => array('18:00', '19:00')
   ),
   //Reservation overlaps
   array(
      'day' => '2017-01-01',
      'time_range' => array('21:00', '22:00')
   ),
   //Reservation overlaps
   array(
      array(
         'day' => '2017-01-01',
         'time_range' => array('23:00', '23:59')
      ),
      array(
         'day' => '2017-01-02',
         'time_range' => array('00:00', '01:00')
     ),
   ),
   ...
);

谢谢!

你想测试一个约会开始是在另一个约会开始和结束之间,然后约会结束也是如此。如果其中任何一个为真,则您有重叠。

使用对象让生活更轻松

首先,你真的应该简化你的结构。如果你真的到处都有日期时间,那么预订应该看起来更像:

class Reservation
{
    /** @var DateTime $start */
    public $start;
    /** @var DateTime $stop */
    public $stop;

    public function _construct(DateTime $start, DateTime $stop): void
    {
        $this->start = $start;
        $this->stop = $stop;
    }

    public function isOverlapping(Reservation $reservation): bool
    {
        if ($reservation->start >= $this->start && $reservation->start <= $this->stop) {
            // starts during reservation
            return true;
        }

        if ($reservation->stop >= $this->start && $reservation->stop <= $this->stop) {
            // ends during reservation
            return true;
        }

        if ($reservation->start <= $this->start && $reservation->end >= $this->stop) {
            // $this is contained by $reservation
            return true;
        }

        return false;
    }
}

以这种方式构建您的预订将允许一些非常简单的解决方案来解决您的问题,例如创建 RecurringReservation,它可以为给定的日期范围生成 Reservations 的数组。或者 Reservation->isToday() 方法可以检查 $start$stop 属性以防午夜结束。

但是对于您提出的问题:

class Reservation
{

    . . .

    public function anyOverlap(array $reservations): bool
    {
        foreach ($reservations as $checkMe) {
            if ($this->isOverlapping($checkMe)) {
                return true;
            }
        }
        return false;
    }
}

$reservation = new Reservation(new DateTime('2017-01-01 20:00'), new DateTime('2017-01-02 02:00'));

$reservations = [
    new Reservation(new DateTime('2017-01-01 18:00'), new DateTime('2017-01-01 19:00')),
    new Reservation(new DateTime('2017-01-01 21:00'), new DateTime('2017-01-01 22:00')),
    new Reservation(new DateTime('2017-01-01 23:00'), new DateTime('2017-01-02 01:00')),
];

$reservation->anyOverlap($reservations); // true

编辑:我刚刚意识到你想要一组重叠的预订:

class Reservation
{

. . .

public function getOverlapping(array $reservations): array
    {
        $result = [];
        foreach ($reservations as $checkMe) {
            if ($this->isOverlapping($checkMe)) {
                $result[] = $checkMe;
            }
        }
        return $result;
    }
}

. . . 

$overlapping = $reservation->getOverlapping($reservations); // array

最后一个附录,使用生成器创建结果列表。

Generators 很棒(如果没有被误用)。当您需要结果列表时,它们可以提供非常高的性能。他们唯一的问题是您必须将他们的结果直接传递到循环中才能获得好处。

public function getOverlapping(array $reservations): Generator
{
    foreach ($reservations as $checkMe) {
        if ($this->isOverlapping($checkMe)) {
            yield $checkMe;
        }
    }
}

...

foreach($reservation->getOverlapping($reservations) as $overlap) {
    yellAtReceptionistFor($overlap); // ... or whatever 
}