PHP:选择时间范围重叠日期的重叠日期时间范围

Posted

技术标签:

【中文标题】PHP:选择时间范围重叠日期的重叠日期时间范围【英文标题】:PHP : Select overlapping date time ranges with time range overlapping day 【发布时间】:2018-03-03 08:40:32 【问题描述】:

我想知道如何让日期时间范围与其他日期时间范围重叠。 特别是日期范围可以有时间范围重叠连续两天。

例如,我有一个定期预订,即 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 的角度来看,我有以下保留意见: 为了便于阅读,日期和时间在此处表示为字符串。实际上它们是日期时间。

//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')
     ),
   ),
   ...
);

谢谢!

【问题讨论】:

你有什么尝试吗? @ishegg 是的,我尝试将预订的每一天的每个时间范围与startA < endB && endA > startB 的所有其他预订的每一天的每个时间范围进行比较。但不确定是否有更好的解决方案。 【参考方案1】:

使用物件让生活更轻松

首先,您确实应该简化您的结构。如果你真的到处都有 dateTimes,那么预订应该看起来更像:

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-&gt;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 

【讨论】:

谢谢!除了 RecurrentReservation 之外,这确实是我已经拥有的一种有趣的方法。【参考方案2】:

你想测试一个约会开始在另一个约会开始和结束之间,那么约会结束也是如此。如果其中任何一个为真,则说明存在重叠。

【讨论】:

以上是关于PHP:选择时间范围重叠日期的重叠日期时间范围的主要内容,如果未能解决你的问题,请参考以下文章

SQL 重叠日期范围

计算日期范围的重叠数量

Postgres 9.x 在重叠日期和时间范围内加入

在 SQL 中检测和合并日期范围的连续重叠

BigQuery 计算两个日期范围重叠

计算多个日期范围内有多少个重叠日期