查找具有自定义间隔且无溢出的 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 代码库贡献者的代码交流。如果他找到更好的解决方案,那么我会更新我的答案。现在,这有效。
我有一个可自定义的 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 代码库贡献者的代码交流。如果他找到更好的解决方案,那么我会更新我的答案。现在,这有效。