Calculate a month later than current month (without rolling over to next month) I needed a script for working out billing dates over a period of time.  The date should be the same day every month however if the billing date starts on a date that doesn't exist in another month, then it should be on the last day of that month. This rules out the DateTimeInterval Object, because that causes months to 'rollover' (31/01 becomes 03/03 etc.) which is unacceptable for my use case.

Try as I might, I couldn't find anything in-built to accommodate for this, so I wrote the following, however it seems like a lot of code for what I'm trying to achieve so I was wondering if anyone could suggest a better / in-built way?

$replaceVars = array(
    'effectivedate' =&gt; '30-9-2014',
    'vatpc'         =&gt; 0.15,
    'paymentamount' =&gt; 150.00,
    'duration'      =&gt; 12
);

$effectivedateObj = new DateTime($replaceVars['effectivedate']);

$iD = array(
    'd' =&gt; $effectivedateObj-&gt;format('d'),
    'm' =&gt; $effectivedateObj-&gt;format('m'),
    'y' =&gt; $effectivedateObj-&gt;format('Y')
);

$payments = '';

for ($i=0; $i &lt; $replaceVars['duration']; $i++) 
{
    $newDate = array(
        'd' =&gt; $iD['d'],
        'm' =&gt; (($iD['m'] + $i) &gt; 12) ? (($iD['m'] + $i) % 12) : $iD['m'] + $i,
        'y' =&gt; (($iD['m'] + $i) &gt; 12 ? $iD['y'] + 1 : $iD['y'])
    );

    if($newDate['d'] &gt; cal_days_in_month(CAL_GREGORIAN, $newDate['m'], $newDate['y']))
    {
        $newDate['d'] = cal_days_in_month(CAL_GREGORIAN, $newDate['m'], $newDate['y']);
    }

    $paymentString = '&lt;tr&gt;';

    $paymentString .= '&lt;td&gt;Payment Number '.($i+1).'&lt;/td&gt;';
    $paymentString .= '&lt;td&gt;'. $newDate['d'] .'/'. $newDate['m'] .'/'. $newDate['y'] .'&lt;/td&gt;';
    $paymentString .= '&lt;td&gt;&amp;pound;'. round(( $replaceVars['paymentamount'] + ( $replaceVars['paymentamount'] * $replaceVars['vatpc'] )), 2) .'&lt;/td&gt;';

    $paymentString .= '&lt;/tr&gt;';

    $payments .= $paymentString;

    echo $paymentString . '&lt;br&gt;';
}


The top array and the lines that build the string are just for testing, but it still seems like a lot of code?

Also I have no idea how necessary my parenthesis are when creating ternary statements, and I do feel a bit neurotic about the amount that I use, so some advice on that would be appreciated.
I'm inclined to keep it simple:

$start = new DateTime('30-Jan-2014', new DateTimeZone("America/Toronto"));
$end = clone $start;
$end-&gt;modify('+1 month');
while (($start-&gt;format('m')+1)%12 != $end-&gt;format('m')%12) {
    $end-&gt;modify('-1 day');
}
echo $end-&gt;format('d-M-Y');


This just checks to see if the month is the expected one, and if not, backs up one day at a time until it is.  The advantage is that it's short, clear, and will work even in leap years (try 2004 to verify that).

Edit: It's also easy to make this into a user-defined function:

function addMonth($begin) 
{
    $end = clone $begin;
    $end-&gt;modify('+1 month');
    while (($begin-&gt;format('m')+1)%12 != $end-&gt;format('m')%12) {
        $end-&gt;modify('-1 day');
    }
    return $end;
}