如何用PHP CarbonPeriod计算多段可能重叠日期的非重叠总天数?
在PHP中使用CarbonPeriod计算多段重叠日期范围的非重叠总天数
需求:计算2段及以上可能存在重叠的日期范围的非重叠总天数,例如以下示例中的3个日期范围,最终需得到非重叠总天数20天。询问Carbon/CarbonPeriod是否提供内置方法实现该需求?
示例代码:
$startDate_1 = '2022-12-01'; $endDate_1 = '2022-12-10'; $startDate_2 = '2022-12-06'; $endDate_2 = '2022-12-15'; $startDate_3 = '2022-12-21'; $endDate_3 = '2022-12-25'; $dateRange_1 = CarbonPeriod::create($startDate_1, $endDate_1); $dateRange_2 = CarbonPeriod::create($startDate_2, $endDate_2); $dateRange_3 = CarbonPeriod::create($startDate_3, $endDate_3);
解答
CarbonPeriod本身没有直接计算多段非重叠日期范围总天数的内置方法,但可以通过合并重叠/相邻区间的方式手动实现,步骤如下:
- 将所有日期范围转换为包含
Carbon实例的数组,方便后续处理 - 按区间的开始日期从小到大排序
- 遍历数组,合并重叠或相邻的区间
- 计算每个合并后区间的天数并求和
具体实现代码
use Carbon\Carbon; use Carbon\CarbonPeriod; // 整理所有日期范围为包含Carbon对象的数组 $ranges = [ ['start' => Carbon::parse($startDate_1), 'end' => Carbon::parse($endDate_1)], ['start' => Carbon::parse($startDate_2), 'end' => Carbon::parse($endDate_2)], ['start' => Carbon::parse($startDate_3), 'end' => Carbon::parse($endDate_3)], ]; // 按开始日期排序 usort($ranges, function($a, $b) { return $a['start']->compare($b['start']); }); // 合并重叠/相邻区间 $mergedRanges = []; foreach ($ranges as $range) { if (empty($mergedRanges)) { $mergedRanges[] = $range; continue; } $last = &$mergedRanges[count($mergedRanges) - 1]; // 当前区间的开始日期小于等于最后一个合并区间的结束日期,说明有重叠/相邻,合并 if ($range['start']->lte($last['end'])) { // 更新合并区间的结束日期为两者中较大的那个 if ($range['end']->gt($last['end'])) { $last['end'] = $range['end']; } } else { $mergedRanges[] = $range; } } // 计算总天数 $totalDays = 0; foreach ($mergedRanges as $range) { // diffInDays返回的是两个日期之间的天数差(不包含结束日),所以需要+1 $totalDays += $range['start']->diffInDays($range['end']) + 1; } echo $totalDays; // 输出:20
代码说明
- 排序确保我们从最早的区间开始处理,避免遗漏重叠情况
- 合并逻辑:如果当前区间的开始日期在最后一个合并区间的范围内(或等于结束日),则合并两个区间,取最大的结束日期作为新的结束日
- 计算天数时,
diffInDays()方法返回的是两个日期之间的间隔天数(例如2022-12-01到2022-12-10返回9),因此需要加1来包含区间的首尾日期,得到正确的天数
内容的提问来源于stack exchange,提问作者Dmitry Chasovnikov
相关产品推荐
相关产品推荐

