I needed too do this today (yes, adding weeks could make this unnecessary). If you find the need to do this, here’s the function I wrote to return a DateTime object, with optional timestamp.
function add_months_to_date( $number_months, $keep_time = true, $date = null ) {
if ( !is_numeric( $number_months ) || intval( $number_months ) < 1 ) {
return null;
}
// Process the $date parameter
try {
$date = new DateTime( $date );
}
// On any error, default to today
catch ( Exception $e ) {
$date = new DateTime();
}
// Get date values for the current month
$day = intval( $date->format( 'd' ) );
$month = intval( $date->format( 'm' ) );
$year = intval( $date->format( 'Y' ) );
// Find the future date month and year
$added_months = $number_months % 12;
$added_years = floor( $number_months / 12 );
$future_month = ( $month + $added_months ) % 12;
$future_year = $year + $added_years + floor( ( $month + $added_months ) / 12 );
// Build future date format, with optional timestamp
$date_format = '%s-%s-01';
if ( $keep_time ) {
$date_format = sprintf( '%s %s', $date_format, $date->format( 'H:i:s' ) );
}
// Find the last day of the future month
$future_date = new DateTime( sprintf( $date_format, $future_year, $future_month ) );
$last_day = $future_date->format( 't' );
// Set out of range dates to the last day of the future month
if ( $last_day < $day ) {
$day = $last_day;
}
// Set the correct day of the future month and return
return $future_date->setDate( $future_year, $future_month, $day );
}