Reputation: 1495
for recurring payment (monthly) i need Same date of the next all months, o.n., the 1st of every month or the 15th of every month (monthly)
but it's should also maintain 31,30,28 and 29th dates.
e.g., 1/31/2014(first recurring payment date) , 2/28/2014 or 2/29/2014(2nd date), 3/31/2014 (3rd ) ... so on )
e.g .1/15/2014 , 2/15/2014 , 3/15/2014 , 4/15/2014 , 5/15/2014 .. so on
Upvotes: 4
Views: 1673
Reputation: 1459
This is an age old question but I have just come up with a solution using Carbon
after asking the same question here.
The reason why DatePeriod()
does not work is that it is always reliant on the previous date in the loop. That is why you get stuck at 29/28 for february and then repeating throughout the rest of the loop.
Here is my solution:
$endDate = CarbonImmutable::parse('10 april 2020')->startOfDay();
$startDate = CarbonImmutable::parse('31 january 2020')->startOfDay();
$interval = CarbonInterval::create('P1M'); // this only works for round 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);
}
I am involved in a bit of a code-off with a contributor of the Carbon codebase. If he finds a better solution then I will update my answer. For now, this works.
Upvotes: 0
Reputation: 7900
You can use DatePeriod class:
This works partially:
There is a issue when d >= 29
, then all months after FEBRUARY will have the payment registered to its last day.
<?php
error_reporting(E_ALL);
$begin = new DateTime('2014-01-29');
$lastDayInterval = DateInterval::createFromDateString('last day of next month');
$monthInterval = new DateInterval('P1M');
$lastDays = new DatePeriod(clone $begin, $lastDayInterval, 12,
DatePeriod::EXCLUDE_START_DATE);
$addedMonthDays = new DatePeriod(clone $begin, $monthInterval, 12,
DatePeriod::EXCLUDE_START_DATE);
$lastDaysArray = array();
foreach ($lastDays as $lastDay) {
$lastDaysArray[] = $lastDay;
}
$addedMonthDaysArray = array();
foreach ($addedMonthDays as $addedMonthDay) {
$addedMonthDaysArray[] = $addedMonthDay;
}
for ($i = 0; $i < 12; $i++) {
if ($addedMonthDaysArray[$i] > $lastDaysArray[$i]) {
echo $lastDaysArray[$i]->format('Y-m-d') . PHP_EOL;
} else {
echo $addedMonthDaysArray[$i]->format('Y-m-d') . PHP_EOL;
}
}
Outputs:
2014-02-28
2014-03-31
2014-04-30
2014-05-31
2014-06-30
2014-07-31
2014-08-31
2014-09-30
2014-10-31
2014-11-30
2014-12-31
2015-01-31
With:
$begin = new DateTime('2014-01-28');
It outputs:
2014-02-28
2014-03-28
2014-04-28
2014-05-28
2014-06-28
2014-07-28
2014-08-28
2014-09-28
2014-10-28
2014-11-28
2014-12-28
2015-01-28
Upvotes: 5