MrMime
MrMime

Reputation: 715

PHP - Intelligent Adding Months to Date

I've just discovered a problem with the usually used method to sum months to a PHP Date. If you search on google or this forum, you usually find somethings like these:

$date = strtotime(date("Y-m-d", strtotime($date)) . " +1 month");

or

$months = DateInterval::createFromDateString('1 month');
$dateInDateTime->add($months);

Both approach are not correct, in my opinion.

For example in my code I have to increment 3 times the month of a starting date beginning with last day of April and return the last day of that months. So my code generates this results:

The second time the script add +1 month to date, goes from 2017-05-31 to 2017-07-01 because 31-05 + 30 days is over the last day of JUNE.

What Im expecting is 06-30 because you are summing MONTHS not DAYS and if you have an overflow, the code has to correct it, not me. This is a common error that explode when you manage February or December (due to change of year).

Im expecting a script that increment month. So if I have 2017-03-23 and sum +1 month, I get 2017-04-23 and if I sum +1 month to 2017-03-31 I got 2017-04-30.

So. Pay attention when using this functions.

Upvotes: 0

Views: 106

Answers (3)

MrMime
MrMime

Reputation: 715

This is the function I wrote:

  //it accept negative month value
public static function realAddMonthsToDate($month,$dateToModify, 
    $dateFormatInput = DEFAULT_SQL_DATE_FORMAT, $dateFormatOutput = DEFAULT_SQL_DATE_FORMAT)
    {
        $currentDate = DateTime::createFromFormat($dateFormatInput, $dateToModify);
        $cDay = $currentDate->format('d');
        $cMonth = $currentDate->format('m');
        $cYear  = $currentDate->format('Y');
        $monthRest = $month;
        $yearOffset = 0;
        if ($month > 12)
        {
            $yearOffset = floor($month / 12);
            $monthRest = $month - ($yearOffset * 12);
        }

        $cMonth += $monthRest;
        if ($cMonth > 12) {
            $cMonth = $cMonth - 12;
            $cYear += 1;
        }
        if ($cMonth <= 0)
        {
            $cMonth = 12 + $cMonth;
            $cYear -= 1;
        }
        $cYear += $yearOffset; 

        $arrivalMonthDays = cal_days_in_month(CAL_GREGORIAN, $cMonth, $cYear);
        if ($cDay >= $arrivalMonthDays) $cDay = $arrivalMonthDays;
        $newDate = new DateTime($cYear.'-'.$cMonth.'-'.$cDay);
        return $newDate->format($dateFormatOutput);
    }

Upvotes: -1

axiac
axiac

Reputation: 72226

What Im expecting is 06-30 because you are summing MONTHS not DAYS and if you have an overflow, the code has to correct it, not me.

PHP corrects it, indeed. It never returns 31st of June as such a date doesn't exist. It corrects it to 1st of July.

What you apparently expect is that when you add 1 month to the last day of a month to get the last day of the next month. But this doesn't make any sense.

What should strtotime('2017-06-30 +1 month') return?

2017-07-31, because you are adding 1 month to the last day of June or 2017-07-30 because you are adding 1 month to the 30th day of June?

The times runs forward, counting the days from the end of the month is not natural. Sometimes it's useful but not that many times. And there always is a better solution: subtract 1 day from the first day of the next month. This way you don't have to do any correction or care about months with different number of days or even about leap years.

Upvotes: 0

Nicolas D
Nicolas D

Reputation: 1222

I think you are trying something dangerous.

What s going on for february? if you want all the time to change only month number it will break for latest days of this month, same for months with 30 days instead of 31...

You have to think about your approach in another way, because changing the month alone won't make an existing date sometimes.

+30 days seems to be the best thing to do

Upvotes: 0

Related Questions