【发布时间】:2013-03-21 02:17:36
【问题描述】:
我有几个DateTime $begin, DateTime $end 形式的日期范围。这些范围可以以各种可能的方式重叠:
|-------|
|=======|
|------|
|======|
|------------|
|=======|
|---|
等等
我想要做的是获取第一个开始到最新一个结束(在上述情况下为第四个)之间的范围的长度(以秒为单位或DateInterval),不包括未涵盖的区域任何范围。
只有两个范围没有问题,但我不知道如何扩展它以处理两个以上。
编辑:
class Range {
public DateTime $begin;
public DateTime $end;
}
$ranges = getRanges(); # function that returns array of Range objects
function getActiveHours($_ranges = array()) {
$sum = 0;
# this is the function I'd like to have
return $sum;
}
只有两个范围,我有一个返回 DateInterval 对象的函数:
function addTimeRanges(DateTime $b1, DateTime $e1, DateTime $b2, DateTime $e2) {
$res = null;
if ($e1 < $b2 || $e2 < $b1) { # separate ranges
$r1 = $b1->diff($e1);
$r2 = $b2->diff($e2);
$res = addIntervals($r1, $r2);
} else if ($b1 <= $b2 && $e1 >= $e2) { # first range includes second
$res = $b1->diff($e1);
} else if ($b1 > $b2 && $e1 < $e2) { # second range includes first
$res = $b2->diff($e2);
} else if ($b1 < $b2 && $e1 <= $e2 && $b2 <= $e1) { # partial intersection
$res = $b1->diff($e2);
} else if ($b2 < $b1 && $e2 <= $e1 && $b1 <= $e2) { # partial intersection
$res = $b2->diff($e1);
}
return $res;
}
其中addIntervals 是一个函数,它返回两个DateInterval 对象之和作为另一个DateInterval 对象。
这是一些基本版本,在我的生产代码中我使用了很多其他不相关的东西。
为了简化,假设我们只有 DateTime 的时间部分:('06:00:00' to '08:00:00'), ('07:00:00' to '09:00:00' '), ('06:00:00', '08:00:00'), ('11:00:00' to '12:00:00') (会有很多这样的范围)。我现在想要的结果是 4 小时(从 6:00 到 9:00 + 从 11:00 到 12:00)。
【问题讨论】:
-
你能提供一些代码吗?
-
这些范围是否存储在数据库中?
-
当你说“第一个的开始......不包括任何范围未涵盖的区域”时,你的意思是你上面的图表代表 两个 答案(因为那个差距在中间)或一个答案(总持续时间减去该间隙的持续时间)?
-
我不精通 php,因此我将答案限制在算法描述上。将所有 DateTime 值排序为一维数组对 ( DateTime, bool ),其中 bool 标记这是否是开始时间。您需要 2 个变量(
depth, curstart ) = (0, undef)。遍历数组,当当前数组元素的布尔分量为真时递增depth,否则递减。每当depth从 0 变为 1 时,将curstart设置为日期时间当前数组元素。每当depth变为0时,添加区间[curstart,] ... -
... 到覆盖区间列表并将
curstart重置为undef。