4

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

Henrique Barcelos
  • 7,670
  • 1
  • 41
  • 66
Yogesh Saroya
  • 1,401
  • 5
  • 23
  • 52
  • possible duplicate of [Timestamps of start and end of month](http://stackoverflow.com/questions/4702696/timestamps-of-start-and-end-of-month) – CBroe Mar 10 '14 at 12:59
  • Possible Duplicate : http://stackoverflow.com/questions/1686724/php-last-day-of-the-month – Rajiv Pingale Mar 10 '14 at 13:00

2 Answers2

5

Edited answer:

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
Community
  • 1
  • 1
Henrique Barcelos
  • 7,670
  • 1
  • 41
  • 66
  • $begin = new DateTime( '2014-01-31' ); Result > 1st > 2014-01-31 2nd > 2014-03-03 ?? it should "2014-02-28" – Yogesh Saroya Mar 10 '14 at 13:12
  • your code same as current date + 30 days , its okay but i want same date of next month, but it's should also maintain 31,30,28 and 29th dates. if first date is 2014-01-31 , then next date should "2014-02-28" – Yogesh Saroya Mar 10 '14 at 13:14
  • @YogeshSaroya I think it's magic, but now it works on PHP 5.4. Can't test in other versions right now... – Henrique Barcelos Mar 10 '14 at 13:33
  • I edited the answer, see if it works for you now. Also, 5.4.4 is a very outdated version, you may suffer from security issues. Try upgrading to 5.4.26. – Henrique Barcelos Mar 10 '14 at 13:35
  • if you pass '2014-01-15' then its should 2014-02-15 , 2014-03-15 current is 2014-01-15 , 2014-02-28 , 2014-03-30 ? – Yogesh Saroya Mar 10 '14 at 13:40
  • Edited again, it **almost** does what you need. With some logic I can't get right now, it should work. – Henrique Barcelos Mar 10 '14 at 15:04
0

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.

Wildcard27
  • 1,437
  • 18
  • 48