我正在开发一项功能,用于检查外部事件是否与内部事件发生冲突(在日历应用中)。这个过程如下:
$internalEvents
)$externalEvents
)中提取外部事件。这些是类型为busy
的现有事件。我试图尽可能地减少它。这是数据输入:
$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"
)
);
现在我尝试通过将内部事件与所有外部事件进行比较来发现任何类型的冲突:
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";
}
}
我正在寻找一种可以找到任何可能的事件重叠冲突的算法。任何提示都受到高度赞赏。
可以找到正在运行的代码here(IDEone.com)。
答案 0 :(得分:4)
两个事件发生碰撞时?请参阅此架构:
----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的开始之后才存在冲突。知道这有助于找到一种有效且简短的方法。比较事件。
关于代码,初步说明:您在代码中多次比较之前转换ISO 8601中的日期,那么为什么不为此创建函数?
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”。请注意,在打印中我只考虑小时:分钟(数组样本中的所有日期都来自同一天),但比较是在完整日期进行的。您可以省略此“打印”键或将其替换为首选输出格式。
您执行两个嵌套foreach
,并为每个循环重新计算日期格式。使用数组样本,可以将DateTime / DateTime :: format调用36次。通过创建一个包含所有已转换$externalEvents
的临时数组,我们可以将这些调用减少到12.因此,在开始foreach()
循环之前,我们将array_map
与上面的自定义函数和$externalEvents
一起使用数组创建一个格式化日期的数组:
$externalDates = array_map( 'eventsToDate', $externalEvents );
然后,我们在foreach()
上启动主$internalEvents
循环:
foreach( $internalEvents as $internalEvent )
{
$internalDates = eventsToDate( $internalEvent );
echo $internalDates['print'] . PHP_EOL;
$result = True;
foreach( $externalDates as $externalDate )
{
此时,我们比较日期。如上所述,我们将start与end和end与start进行比较。为了简化下一次比较,我们使用strcmp
,一个“返回&lt;如果str1小于str2,则为0;如果str1大于str2,则为0;如果它们等于“:
$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";
}
}
最后,我们可以打印结果:
echo "Result: " . ( $result ? 'OK' : 'NOT OK') . "\n\n";
}
的 eval.in demo 强>
注意:通过上面的比较,我们得到以下结果{<1}}:
$internalEvent
相反,如果你想要这个结果:
12:00-13:00
08:00-12:00 OK
15:30-16:00 OK
13:30-14:15 OK
Result: OK
您必须将以上12:00-13:00
08:00-12:00 COLLIDE
15:30-16:00 OK
13:30-14:15 OK
Result: NOT OK
条件替换为:
if
以上代码可以使用,如果您想了解有关碰撞类型的更多详细信息,可以在 if( $startCmp<=0 && $endCmp>=0 )
条件内测试其他开始/结束组合。
替代方案:返回碰撞事件
如果 - 而不是打印结果 - 您想要捕获碰撞事件,您可以用这种方式用if
替换嵌套的foreach()
:
array_filter
此时,碰撞事件在数组$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 );
}
);
中。显然,数组是空的,没有碰撞。