PHP dates – add and subtract months…. really!

I needed to add and subtract just one month from a date in PHP. Sounds easy right…  wrong!

Doing this won’t work:

$date = new DateTime('2013-05-31');
$date->modify('-1 months');
echo $date->format('Y-m-d'); // 2013-05-01

Neither will this:

$date = date_create('2013-05-31');
date_sub($date, date_interval_create_from_date_string('10 days'));
echo date_format($date, 'Y-m-d'); // gives you 2013-05-01

…and there are a few other ways to do this but none of them will work. Users on many forums argue that this behaviour of returning the wrong date is normal and correct. BUT come on – we are humans and know that the date should be 2013-04-30. Why don’t the good folks in the PHP code world give us another function to return ‘human’ dates!

After searching on forums I found what I was looking for:
http://stackoverflow.com/questions/10724305/how-to-add-1-month-on-a-date-without-skipping-i-e-february

Only problem was that that thread only gave me the add function.  So I created the subtract function too.  Here they are for your coding pleasure:

ADD

$monthToAdd = 1;
$d1 = DateTime::createFromFormat('Y-m-d H:i:s', $date);

$year = $d1->format('Y');
$month = $d1->format('n');
$day = $d1->format('d');

$year += floor($monthToAdd/12);
$monthToAdd = $monthToAdd%12;
$month += $monthToAdd;
if($month > 12) {
    $year ++;
    $month = $month % 12;
    if($month === 0)
        $month = 12;
}

if(!checkdate($month, $day, $year)) {
    $d2 = DateTime::createFromFormat('Y-n-j', $year.'-'.$month.'-1');
    $d2->modify('last day of');
}else {
    $d2 = DateTime::createFromFormat('Y-n-d', $year.'-'.$month.'-'.$day);
}
$d2->setTime($d1->format('H'), $d1->format('i'), $d1->format('s'));

echo $d2->format('Y-m-d H:i:s');

SUBTRACT

$monthToAdd = 1;
$d1 = DateTime::createFromFormat('Y-m-d H:i:s', $date);

$year = $d1->format('Y');
$month = $d1->format('n');
$day = $d1->format('d');

$year -= floor($monthToSubtract/12);
$monthToSubtract = $monthToSubtract%12;
$month -= $monthToSubtract;
if($month < 1) {
    $year --;
    $month = $month % 12;
    if($month === 0)
        $month = 12;
}

if(!checkdate($month, $day, $year)) {
  $d2 = DateTime::createFromFormat('Y-n-j', $year.'-'.$month.'-1');
  $d2->modify('last day of');
}else {
  $d2 = DateTime::createFromFormat('Y-n-d', $year.'-'.$month.'-'.$day);
}
$d2->setTime($d1->format('H'), $d1->format('i'), $d1->format('s'));

echo $d2->format('Y-m-d H:i:s');

You can turn these into functions yourself but man that is a lot of code for something so simple… So come on… really PHP?

On the bright side I’m a better coder because of this. Ha!  or maybe not… 🙂

Comments: