查找具有自定义间隔且无溢出的 2 个日期之间的日期

Find dates between 2 dates with custom interval and without overflow

我有一个可自定义的 DateInterval 对象,以及一个可自定义的开始和结束日期。我想使用间隔找到开始和结束之间的日期。我正在使用 Carbon 来尝试帮助解决这个问题。

问题来了: 我的时间间隔是几个月,但开始日期 > 28 我无法使用 CarbonPeriod.

控制溢出

这是我正在测试的代码:

$di = CarbonInterval::create('P1M');
$start = Carbon::parse('31 january 2020')->startOfDay();
$end = Carbon::parse('01 april 2020')->startOfDay();

$period = CarbonPeriod::create($start, $di, $end);

$items = [];
foreach ($period as $item) {
    $items[] = $item;
}

我希望以上结果

2020-01-31
2020-02-29
2020-03-30

但是我明白了

2020-01-31
2020-03-02
2020-04-02

请记住,DateInterval 是可自定义的(或者我只使用 Carbon::addMonthNoOverflow())。

任何人都可以帮助我如何实现上述目标吗?

tl;dr:它不适用于 Carbon。

日期算法很难表达,也更难正确。因此,例如,当开始日期例如在二月时,您甚至无法表达您想要间隔中的“一个月的最后一天”:

start: 2020-02-29
P1M -> 2020-03-29 (not end of month, obviously)

单独一个区间无法表达你想要月末的语义。

即使您会找到一种方法让溢出正常工作,这样您就不会在下个月的月初结束,这个问题将继续存在。 (我尝试了一些方法都失败了)

您(向用户)所能提供的只是对日期数组应用额外的函数来实现您的目标,例如 $item->endOfMonth()。但是您仍然需要注意开始的日是 <= 28.

具有讽刺意味的是,您可以对所有 Carbon、CarbonInterval 和 CarbonPeriod 调用 ->settings(['monthOverflow'=>false, 'yearOverflow'=>false]),但它没有效果(除非您对其调用 addMonth() ,这是相当令人失望的,它不会应用于 $start->add($di))。这归结为 Carbon 最终只是标准 DateTime 对象的包装器,它也不支持溢出。

长话短说,Carbon(当前版本)没有优雅的 and/or 简单解决方案。 ;o/

经过大量挖掘,并意识到@Jakumi 是正确的,在问了同样的问题 here

之后,我刚刚提出了使用 Carbon 的解决方案

DatePeriod() 不起作用的原因是它总是依赖于循环中的前一个日期。这就是为什么您在 2 月停留在 29/28,然后在整个循环的其余部分重复。

这是我的解决方案:

$endDate = CarbonImmutable::parse('10 april 2020')->startOfDay();
$startDate = CarbonImmutable::parse('31 january 2020')->startOfDay();
$interval = CarbonInterval::create('P1M'); // only works for number of months, not composite intervals


$workingDate = $startDate->copy();

for ($i = 1; $workingDate <= $endDate; $i = $i + $interval->m) {
    echo = $workingDate->format('Y-m-d') . "\n";
    $workingDate = $startDate->addMonthsNoOverflow($i);
}

我参与了一些与 Carbon 代码库贡献者的代码交流。如果他找到更好的解决方案,那么我会更新我的答案。现在,这有效。