I need to generate multiple dates between two dates, based on an interval (eg. weekly, every 2 weeks, monthly, ...), but I only need the ones in the future.
For that I'm using the Carbon 2.72.3 library and I came up with the following code:
$interval = CarbonInterval::week();
$startDate = Carbon::create(2024, 3, 17);
$endDate = Carbon::create(2024, 4, 1);
$amount = 4;
$period = CarbonPeriod::interval($interval)
->setStartDate($startDate)
->addFilter(fn(Carbon $carbon) => $carbon->isFuture(), 'isFuture')
->addFilter(fn(Carbon $carbon) => !$endDate || $carbon->isBefore($endDate))
->setRecurrences($amount);
dd($period->toArray());
Unfortunately, this just works sometimes (meaning: Not with all dates / intervals) and I can't tell exactly under what conditions it works and when it doesn't work.
With the dates above, Carbon throws an Carbon\Exceptions\UnreachableException
with the message Could not find next valid date.
. If I reduce the amount to 1, it works, but only returns 2024-03-24 as a date (which is then expected, but doesn't solve my issue).
With other data, such as this, it works as expected: It returns 4 dates according to the interval in relation to the start date, but only the ones in the future.
$interval = CarbonInterval::month();
$startDate = Carbon::create(2022, 6, 2);
$endDate = null;
$amount = 4;
If I set the $endDate = Carbon::create(2024, 4, 1);
, it also stops working. I suspect that it sometimes happens when it cannot generate the $amount
of dates. But this doesn't seem to be the case always, as the following setup should be able to generate at least 4 dates, though the same exception is thrown:
$interval = CarbonInterval::year();
$startDate = Carbon::create(2022, 6, 2);
$endDate = Carbon::create(2028, 4, 1);
$amount = 4;
Expected dates:
Though in this case it only works if I set the end date to 2029-04-01, which doesn't make sense to me as the last date is already in 2027.
To set the end date, Carbon provides the setEndDate()
method, so your code should be like this:
$period = CarbonPeriod::interval($interval)
->setStartDate($startDate)
->addFilter(fn(Carbon $carbon) => $carbon->isFuture(), 'isFuture')
->setEndDate($endDate)
->setRecurrences($amount);
Note that setEndDate()
accepts a null
value.