The "add a month", etc., annoyance due to different month lengths is, indeed, irritating.
The solution, if you have PHP >= 5.2, is the DateTime class.
Though it is simple to use this class to obtain total control, it is not entirely trivial.
Here is one version of correct code to add a month.
// Variables defining the start date
// Example only - this could be any valid date
$year = '2013';
$month = '01';
$day = '31';
// set to the desired starting date and time
$the_date = new DateTime($year . '-' . $month . '-' . $day);
// Jump to the first day of this month
$the_date->modify("first day of this month");
// add 14 days, so we'll land on the 15th
$the_date->add(new DateInterval("P14D"));
// add 1 month - guaranteed to work!
$the_date->add(new DateInterval("P1M"));
// calculate how many days to add to 15 to get back to the **day** we started with...
// (as an integer, regardless of whether it is a valid day of the current month)
$number_days_to_add_back = intval($day) - 15;
// determine the last day of the month stored in $the_date
$test_last_date = clone $the_date;
$test_last_date->modify("last day of this month");
$day_last = $test_last_date->format('j'); // This provides the day, 01-31
// Test if adding $number_days_to_add_back runs past
// the end of the month; if so, adjust it so it won't run past
// the last day of the month
if (15 + $number_days_to_add_back > intval($day_last)) {
$number_days_to_add_back = intval($day_last) - 15;
}
// Now make the final adjustment
$the_date->modify("" . $number_days_to_add_back . " day");
// Test it - a month has been added
$test = date_format($the_date, 'Y-m-d');