使用PHP检测日历事件重叠冲突 [英] Detect calendar event overlap conflicts using PHP

查看:82
本文介绍了使用PHP检测日历事件重叠冲突的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在开发一项功能,用于检查外部事件是否与内部事件(在日历应用程序中)发生冲突。该过程如下所示:

I am working on a feature that checks if external events collide with internal events (in a calendar app). The process looks like this:


  • 我的应用程序创建了一系列可能的事件(称为 $ internalEvents

  • 我从日历中获取外部事件,例如Google日历,iCloud等(称为 $ externalEvents )。这些是类型为 busy 的现有事件。

  • 现在,我必须检查内部和外部事件是否存在任何冲突。我尝试了一些操作,如下所示,但这是不正确的或防弹的。

  • My application creates an array of possible events (called $internalEvents)
  • I source external events from calendars such as Google Calendar, iCloud, etc. (called $externalEvents). These are existing events with the type busy.
  • Now I have to check if there is any kind of conflict regarding internal and external events. I tried something as you can see below but this is by far not correct or bulletproof.

我试图将其尽可能减少到最小。这是数据输入:

I tried to reduce it to the minimum as much as possible. This is the data input:

$internalEvents = array(
    array(
        "start" => "03/29/2016 12:00:00",
        "end" => "03/29/2016 13:00:00"
    ),
    array(
        "start" => "03/29/2016 12:30:00",
        "end" => "03/29/2016 13:30:00"
    ),
    array(
        "start" => "03/29/2016 13:00:00",
        "end" => "03/29/2016 14:00:00"
    ),
    array(
        "start" => "03/29/2016 13:30:00",
        "end" => "03/29/2016 14:50:00"
    ),
    array(
        "start" => "03/29/2016 14:00:00",
        "end" => "03/29/2016 15:00:00"
    ),
    array(
        "start" => "03/29/2016 14:30:00",
        "end" => "03/29/2016 15:30:00"
    ),
    array(
        "start" => "03/29/2016 15:00:00",
        "end" => "03/29/2016 16:00:00"
    ),
    array(
        "start" => "03/29/2016 15:30:00",
        "end" => "03/29/2016 16:30:00"
    ),
    array(
        "start" => "03/29/2016 16:00:00",
        "end" => "03/29/2016 17:00:00"
    )
);

$externalEvents = array(
    array(
        "start" => "03/29/2016 08:00:00",
        "end" => "03/29/2016 12:00:00",
        "type" => "busy"
    ),
    array(
        "start" => "03/29/2016 15:30:00",
        "end" => "03/29/2016 16:00:00",
        "type" => "busy"
    ),
    array(
        "start" => "03/29/2016 13:30:00",
        "end" => "03/29/2016 14:15:00",
        "type" => "busy"
    )
);

现在,我尝试通过将内部事件与所有外部事件进行比较来发现任何类型的冲突:

Now I try to find any kind of conflict by comparing the internal event to all external events:

foreach($internalEvents as $internalEvent) {

    $internalEventStart = new DateTime($internalEvent['start']);
    $internalEventEnd = new DateTime($internalEvent['end']);

    $result = true;

    echo "\nverifying " . $internalEventStart->format('Y-m-d H:i') . " - " . $internalEventEnd->format('Y-m-d H:i') . "\n";

    foreach($externalEvents as $externalEvent) {
        $externalEventStart = new DateTime($externalEvent['start']);
        $externalEventEnd = new DateTime($externalEvent['end']);

        // check if there are conflicts between internal and external events
        if ($internalEventStart >= $externalEventStart && $internalEventStart <= $externalEventEnd) {
            $result = false;
            echo "   problem 1: event is between busy time: " . "\n";
        }

        if ($internalEventStart >= $externalEventStart && $internalEventStart <= $externalEventEnd && $externalEventEnd <= $internalEventEnd) {
            $result = false;
            echo "   problem 2: event starts during busy time: " . "\n";
        }

        if ($internalEventStart <= $externalEventStart && $externalEventStart <= $internalEventEnd && $internalEventEnd <= $externalEventEnd) {
            $result = false;
            echo "   problem 3: event stops during busy time: " . "\n";
        }

        if (($internalEventStart <= $externalEventStart) && ($externalEventStart <= $externalEventEnd) && ($externalEventEnd <= $internalEventEnd)) {
            $result = false;
            echo "   problem 4: event during busy time: " . "\n";
        }

        if (($internalEventStart <= $internalEventEnd) && ($internalEventEnd <= $externalEventStart) && ($externalEventStart <= $externalEventEnd)) {
            $result = false;
            echo "   problem 5: event during busy time: " . "\n";
        }
    }

    if($result) {
        echo "   result: OK\n";
    } else {
        echo "   result: NOT OK \n";
    }
}

我正在寻找一种可以找到任何可能的算法事件重叠冲突。

I am looking for an algorithm that can find any possible event overlap conflict. Any hint is highly appreciated.

可以在此处(IDEone.com)。

The running code can be found here (IDEone.com).

推荐答案

两个事件发生冲突时?请参见以下模式:

When two events collide? See this schema:

                              ----E----                 CS/EE   CE/ES
                        --N--                             <       <
                                        --N--             >       >
                           --C--                          <       >
                                     --C--                <       >
                                --C--                     <       >
                             -----C-----                  <       >
                    ··················································
                    E  = Main Event
                    N  = Not Collide Event
                    C  = Collide Event
                    CS = Compare Event Start
                    EE = Main Event End
                    CE = Compare Event End
                    ES = Main Event Start

如您所见,仅当事件C的开始在事件E的开始之前且事件E的结束在事件C的开始之后才是冲突。知道这有助于找到一种有效且简短的方法来比较事件。

As you can see, there is collision only when the start of event C is before the end of event E and the end of event E is after the start of event C. Knowing this helps to find an efficient and short method to compare events.

关于代码,有一个初步说明:您先在ISO 8601中转换日期,然后在代码中进行多次比较,所以为什么不为此创建函数?

About the code, a preliminary note: you convert the dates in ISO 8601 before comparing it multiple times in your code, so why do not create a function for this?

function eventsToDate( $row )
{
    $retval = array( 'start' => date_create( $row['start'] )->format('Y-m-d H:i:s'), 'end' => date_create( $row['end'] )->format('Y-m-d H:i:s') );
    $retval['print'] = sprintf( '%s-%s', substr( $retval['start'],-8,5 ), substr( $retval['end'],-8,5 ) );
    return $retval;
}

此函数返回一个关联数组,其中包含 start和 end格式。我添加了第三个键 print,以便在调试期间使用。请注意,在打印中,我只考虑小时:分钟(数组样本中的所有日期都来自同一天),但是比较是在完整日期进行的。您可以忽略此打印键,也可以将其替换为首选的输出格式。

This function returns an associative array with 'start' and 'end' in your format. I have added a third key, 'print', to use during debug. Note that in the print I consider only hours:minutes (all the dates in your array sample are from same day), but the comparison is made on complete dates. You can omit this 'print' key or replace it with preferred output format.

您执行两个嵌套的 foreach ,并为每个循环重新计算日期格式。对于数组样本,您可以调用DateTime / DateTime :: format 36次。通过创建一个包含所有转换后的 $ externalEvents 的临时数组,我们可以将这些调用减少到12个。因此,在启动 foreach()循环,我们将 array_map 与上述自定义函数一起使用,并使用 $ externalEvents 数组创建具有格式化日期的数组:

You execute two nested foreach, and for each loop recalculate the date format. With your array samples, you call DateTime/DateTime::format 36 times. By creating a temporary array with all converted $externalEvents, we can reduce these calls to 12. So, before starting foreach() loop, we use array_map with above custom function and $externalEvents array to create an array with formatted dates:

$externalDates = array_map( 'eventsToDate', $externalEvents );

然后,我们开始主要的 foreach() $ internalEvents 上循环:

Then, we start the main foreach() loop on $internalEvents:

foreach( $internalEvents as $internalEvent )
{
    $internalDates = eventsToDate( $internalEvent );

    echo $internalDates['print'] . PHP_EOL;
    $result = True;
    foreach( $externalDates as $externalDate )
    {

我们比较日期。如上所述,我们将开始与结束与开始进行比较。为了简化下一个比较,我们使用 strcmp ,这是一个返回<如果str1小于str2,则为0;如果str1大于str2,则为0;如果它们相等,则为0:

At this point, we compare dates. As mentioned above, we compare start with end and end with start. To simplify next comparison, we use strcmp, a php function that "returns < 0 if str1 is less than str2, > 0 if str1 is greater than str2, and 0 if they are equal":

        $startCmp = strcmp( $internalDates['start'], $externalDate['end'] );
        $endCmp   = strcmp( $internalDates['end'],   $externalDate['start']   );

现在,比较:

        if( $startCmp<0 && $endCmp>0 )
        {
            $result = False;
            echo "           {$externalDate['print']} COLLIDE\n";
        }
        else
        {
            echo "           {$externalDate['print']} OK\n";
        }
    }

最后,我们可以打印结果:

And, finally, we can print the result:

    echo "Result: " . ( $result ? 'OK' : 'NOT OK') . "\n\n";
}

eval.in演示

注意:进行以上比较,首先 $ internalEvent 我们获得以下结果:

Note: with above comparison, with first $internalEvent we obtain following result:

12:00-13:00
           08:00-12:00 OK
           15:30-16:00 OK
           13:30-14:15 OK
Result: OK

相反,如果您想要此结果:

Instead, if you want this result:

12:00-13:00
           08:00-12:00 COLLIDE
           15:30-16:00 OK
           13:30-14:15 OK
Result: NOT OK

如果 c $ c>条件:

You have to replace above if condition with this:

         if( $startCmp<=0 && $endCmp>=0 )

上面的代码将起作用,如果您想了解更多有关碰撞类型的详细信息,可以测试start的其他组合/ end在如果条件下。

Above code will work, if you want more details about kind of collision, you can test other combinations of start/end inside if condition.

替代:返回碰撞事件

如果-而不是打印结果-如果您想捕获冲突事件,可以通过以下方式将嵌套的 foreach()替换为 array_filter

If — instead of printing results — you want catch colliding events, you can replace nested foreach() with array_filter in this way:

$result = array_filter
(
    $externalDates, 
    function( $row ) use( $internalDates )
    {
        $startCmp = strcmp( $internalDates['start'], $row['end'] );
        $endCmp   = strcmp( $internalDates['end'],   $row['start']   );
        return( $startCmp<0 && $endCmp>0 );
    }
);

此时,碰撞事件位于数组 $ result 。显然,是数组为空,没有冲突。

At this point, colliding events are in array $result. Obviously, is the array is empty, there are not collisions.


  • 了解有关< a href = http://php.net/manual/zh/function.strcmp.php rel = noreferrer> strcmp()

  • 了解有关< a href = http://php.net/manual/zh/function.array-map.php rel = noreferrer> array_map()

  • 了解更多关于 array_filter()

  • Read more about strcmp()
  • Read more about array_map()
  • Read more about array_filter()

这篇关于使用PHP检测日历事件重叠冲突的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆