phpdaterecurly

PHP - How to calculate one month or one year later


I'd like to calculate next billing date of Recurly plan in PHP. There are 2 types of billing cycle: yearly | monthly. I tried to use DateTime and DateInterval classes, but didn't get expected results.

<?php
$referenceTime = new \DateTime('2016-01-31');
$oneMonthLater = $referenceTime->modify('+1 month');
var_dump($oneMonthLater);
// public 'date' => string '2016-03-02 00:00:00.000000'

Adding one month to the 31st of Januray gives me the second of March and not the 29th (or 28th) of February as I would expect.

The same for the 31st of August:

<?php
$referenceTime = new \DateTime('2016-08-31');
$oneMonthLater = $referenceTime->modify('+1 month');
var_dump($oneMonthLater);
// public 'date' => string '2016-10-01 00:00:00.000000'

If yearly, I expect Feb 29, 2016 + 1 year => Feb 28, 2017

Thanks.


Solution

  • function get_next_billing_date($now, $type, $format = 'Y-m-d')
    {
        $date = new DateTime($now);
    
        $y = $date->format("Y");
        $m = $date->format("m");
        $d = $date->format("d");
    
        if ($type == 'year') {
            $y++;
            if ($m == 2 && $d == 29) {
                $d = 28;
            }
        } else if ($type == 'month') {
            if ($m == 12) {
                $y++;
                $m = 1;
            } else {
                $m++;
            }
    
            $first_date = sprintf("%04d-%02d-01", $y, $m);
            $last_day_of_next_month = date('t', strtotime($first_date));
    
            if ($d > $last_day_of_next_month) {
                $d = $last_day_of_next_month;
            }
        } else {
            // not supported
            return false;
        }
    
        $date->setDate($y, $m, $d);
    
        return $date->format($format);
    }