检查两个时间是否重叠。

11
我想确定从数据库读取的时间与用户提供的时间重叠。我的数据库如下所示:
-----------------------------------------------
|organiser|meeting_start|meeting_end|boardroom|
-----------------------------------------------
| John Doe| 1340193600  | 1340195400| big     |
-----------------------------------------------

我的代码长这样:

date_default_timezone_set('Africa/Johannesburg');
$from = strtotime($_GET['meeting_date'] . ' ' . $_GET['meeting_start']);
$to = strtotime($_GET['meeting_date'] . ' ' . $_GET['meeting_end']);
$another_meeting = false;
$meeting_date = strtotime($_GET['meeting_date']);
$meeting_next = $meeting_date + 86400;

$result = mysql_query("SELECT meeting_start, meeting_end FROM admin_boardroom_booking WHERE boardroom = '" . $_GET['boardroom'] . "' AND meeting_start >= '" . $meeting_date . "' AND meeting_end < '" . $meeting_next . "'")or die(mysql_error());
while($row = mysql_fetch_array($result)) {
    $from_compare = $row['meeting_start'];
    $to_compare = $row['meeting_end'];

    $intersect = min($to, $to_compare) - max($from, $from_compare);
    if ( $intersect < 0 )
        $intersect = 0;

    $overlap = $intersect / 3600;
    if ( $overlap <= 0 ) {
        $another_meeting = true;
        break;
    }
}

if ($another_meeting)
    echo 'ERROR';

如果我故意输入两个重叠的时间,它不会报错。我做错了什么?


3
您的代码存在 SQL 注入漏洞。阅读此链接:https://dev59.com/onRB5IYBdhLWcg3wgXdV 并改为使用参数化查询。旧的 mysql_ 函数即将被弃用,您应该使用 MySQLi 代替。 - Polynomial
谢谢你的帮助。但是那并没有解决我的问题。 - Bird87 ZA
@DarkRanger:你能在 while 循环之前打印出 echo $result,并让我知道你得到了什么吗? - Fahim Parkar
6个回答

35

当且仅当以下至少一种情况成立时,两个时间段 P1 和 P2 重叠:

  1. P1 的开始时间在 P2 的开始和结束时间之间 (P2.from <= P1.from <= P2.to)
  2. P2 的开始时间在 P1 的开始和结束时间之间 (P1.from <= P2.from <= P1.to)

这将包含部分重叠的时间段以及一个时间段完全覆盖另一个时间段的情况。如果两个时间段重叠,则其中一个时间段必须始终在另一个时间段内部开始(或结束)。

因此,$another_meeting 的定义如下:

$another_meeting = ($from >= $from_compare && $from <= $to_compare) ||
                   ($from_compare >= $from && $from_compare <= $to);

如果一个事件可以在另一个事件结束的正好同一时间开始,您可能希望将边界情况更改为严格的<检查。


1
这似乎完美地运作着。我稍微改了一下,以允许精确相同的时间(我正在与工程师合作,指定14:01而不仅仅是14:00似乎是一个重大的错误)。Baie Dankie(非洲语中的非常感谢您)。 - Bird87 ZA

5
我正在做类似的事情...但只是与时间相关...
$startTime = strtotime("7:00");
$endTime   = strtotime("10:30");

$chkStartTime = strtotime("10:00");
$chkEndTime   = strtotime("12:10");

if($chkStartTime > $startTime && $chkEndTime < $endTime)
{
    // Check time is in between start and end time
    echo "1 Time is in between start and end time";
}
elseif(($chkStartTime > $startTime && $chkStartTime < $endTime) || ($chkEndTime > $startTime && $chkEndTime < $endTime))
{
    // Check start or end time is in between start and end time
    echo "2 ChK start or end Time is in between start and end time";
}
elseif($chkStartTime==$startTime || $chkEndTime==$endTime)
{
    // Check start or end time is at the border of start and end time
    echo "3 ChK start or end Time is at the border of start and end time";
}
elseif($startTime > $chkStartTime && $endTime < $chkEndTime)
{
    // start and end time is in between  the check start and end time.
    echo "4 start and end Time is overlapping  chk start and end time";
}

2
我可能会用以下方式解决它:
function avaliable($start, $end) {
  // checks if there's a meeting between start or end
  $q = "SELECT * FROM admin_boardroom_booking "
    . "WHERE NOT (meeting_start BETWEEN '$end' AND '$start' "
    . "OR meeting_end BETWEEN '$end' AND '$start')";
  $result = mysql_query($q);

  // returns true on no conflicts and false elseway
  return mysql_num_rows($result) === 0;
}

可能会有作用,但我还需要检查一下会议是否完全重叠另一个会议。例如:会议1开始,会议2开始,会议2结束,会议1结束。根据你的代码来看,在这种情况下它不会返回false... - Bird87 ZA
你的条件不幸是错误的。如果$start在meeting_start之前,$end在meeting_end之后会发生什么?括号将计算为“false”,然后你将其取反为“true”!这也是一种相当奇怪的方法,当你只想知道是否存在冲突时,就获取所有非冲突的会议。尝试匹配有冲突的会议,使用COUNT(*)LIMIT 1来加快速度。 - Emil Vikström
好像我有点累了,我要重写一下 >___< - nyson

0

Emil Vikström的答案是正确的,但需要考虑一种情况。
例如,一个时间范围是另一个时间范围的子集。
因此,假设P1{start_time, end_time}P2{start_time, end_time}将在以下任何情况下重叠。

  • P1.start_time <= P2.start_time <= P1.end_time
  • P1.start_time <= P2.end_time <= P1.end_time
  • P2.start_time <= P1.start_time <= P1.end_time <= P2.end_time

只是假设时间按升序排序。以下是示例:

|-----------------------------------|
|  Start time  |   End time  | Name |
|-----------------------------------|
|    10:00     |    14:00    |  P1  |
|-----------------------------------|
|    12:00     |    16:00    |  P2  |
|-----------------------------------|
|    08:00     |    12:00    |  P3  |
|-----------------------------------|
|    07:00     |    16:00    |  P4  |
|-----------------------------------|

如果您将P1视为基准时间,并想要检查P2、P3、P4是否与其重叠。

  1. P1.start_time <= P2.start_time <= P1.end_time true
  2. P1.start_time <= P3.end_time <= P1.end_time true
  3. P4.start_time <= P1.start_time <= P1.end_time <= P4.end_time true

这是您可以检查任何时间是否与另一个时间重叠的方法。


1
我的答案已经解决了这个问题。如果P1是P2的一个完整子集,那么我的第一个条件成立:P1将在P2内开始。 - Emil Vikström

0
如果$to始终晚于$from,我们可以使用这种更短的解决方案。
$another_meeting = !($from > $to_compare || $from_compare > $to);

0

我的解决方案处理了边缘情况,而且非常紧凑:

    private function meetingOverlap($meeting1, $meeting2){
        if ($meeting1['start']>$meeting2['start'] && $meeting1['start']<$meeting2['end'] ) return true;
        if ($meeting2['start']>$meeting1['start'] && $meeting2['start']<$meeting1['end'] ) return true;
        if ($meeting2['start']==$meeting2['start'] && $meeting1['end']==$meeting2['end'] ) return true;
        return false;
    }

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接