22

Possible Duplicate:
PHP DateTime::modify adding and subtracting months

I have a starting date (i.e. 2011-01-30) and want to add 1 month.

The problem is in defining what a month is. So if I use the following code:

$d1 = DateTime::createFromFormat('Y-m-d H:i:s', '2011-01-30 15:57:57');
$d1->add(new DateInterval('P1M'));
echo $d1->format('Y-m-d H:i:s');

I get the following result: 2011-03-02 15:57:57

The problem is, that I need it to use the following rules:

  • If I add 1 month it will just add 1 on the month part and leave the day part (2011-01-15 will become 2011-02-15)
  • If the day is not existing in the month we will end, we take the last existing day of it (2011-01-30 will become 2011-02-28)

Is there a common function in php that can do this or do I have to code it by myself? Maybe I'm just missing a parameter or something!?

Community
  • 1
  • 1
Kasihasi
  • 1,062
  • 1
  • 8
  • 20
  • 4
    If you are iterating multiple months, you will need to be careful that you don't get "stuck" on the 28th. e.g., If you Start at 1/30 .. 2/28 .. 3/28, when you probably want 3/30. – Matthew May 23 '12 at 16:47
  • 4
    I've recently dealt with this implementing a commercial billing system. There isn't a pre-baked solution. You have to keep track of your starting day, increment the month, see if that day exists in the next month, if not, use the last day of the month. It's actually not terribly complicated, but it will feel like you shouldn't have to write that much code. – Endophage May 23 '12 at 19:18
  • The requirement here is to add *calendar months*. Jump to the 1st of the month, add on the months you want, then jump back to the day number you started on *or* the last day of the new month if the month is shorted than the starting day number. – Jason Apr 19 '16 at 15:12

3 Answers3

14

It seems that there is no ready function for it, so I wrote it by myself. This should solve my problem. Thanks anyway for your answers and comments. If you will find some errors, please provide in the comments.

This function calculates the month and the year I will end in after adding some month. Then it checks if the date is right. If not, we have the case that the day is not in the target month, so we take the last day in the month instead. Tested with PHP 5.3.10.

<?php
$monthToAdd = 1;

$d1 = DateTime::createFromFormat('Y-m-d H:i:s', '2011-01-30 15:57:57');

$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');
Kasihasi
  • 1,062
  • 1
  • 8
  • 20
  • 1
    Perfect. I needed this but for mutually exclusive time periods - I just added `$day = $day-1;` after `$day=$d1->format('d');` – Simeon Apr 20 '16 at 10:06
  • 1
    Some issue with negative values : Replace `$year += floor($monthToAdd/12);` by `$year += $monthToAdd>= 0 ? floor($monthToAdd/ 12) : ceil($monthToAdd/12);` and before `if(!checkdate())`, add this : `if( $month < 0 ) { $month = 12 + $month % 12; $year--; }`. Tested with PHP 5.6.29. Hope it helps someone. – sk001 May 09 '17 at 10:02
11

You have several alternatives besides DateInterval.

Here are examples that use strtotime():

http://www.brightcherry.co.uk/scribbles/php-adding-and-subtracting-dates/

// Subtracting days from a date
$date = "1998-08-14";
$newdate = strtotime ( '-3 day' , strtotime ( $date ) ) ;
$newdate = date ( 'Y-m-j' , $newdate );

echo $newdate;

...

// Subtracting months from a date
$date = "1998-08-14";
$newdate = strtotime ( '-3 month' , strtotime ( $date ) ) ;
$newdate = date ( 'Y-m-j' , $newdate );

echo $newdate;

Here's are some links for DateInterval:

Q: Exactly how do you define a "month"?

Def#1): a "month" is a "month" - regardless of #/days

Def#2): a "month" is 30 days (for example)

Def#3): a "month" is the #/days between the 1st Monday of subsequent months

etc. etc

Q: What exactly is your "definition"?

Community
  • 1
  • 1
paulsm4
  • 114,292
  • 17
  • 138
  • 190
  • 1
    Well same here. If i try: $date = "2011-01-30"; $newdate = strtotime ( '+1 month' , strtotime ( $date ) ) ; echo date('Y-m-d', $newdate); I get 2011-03-02 and not 2011-02-28 as I need – Kasihasi May 23 '12 at 16:49
  • As you could see from my question I used your Def#1. – Kasihasi May 24 '12 at 08:21
  • 2
    If you are using strtotime() then the best way to avoid skipping February is to add $date = strtotime('first day of +1 month'); – Primoz Rome Jan 29 '15 at 07:34
0

OK - as far as I can tell, you still haven't clearly defined what you mean by "month".

But here's one more function that might help:

http://php.net/manual/en/function.getdate.php

/* Function to find the first and last day of the month from the given date.
*
* Author Binu v Pillai            binupillai2003@yahoo.com
* @Param            String             yyyy-mm-dd
*
*/
function findFirstAndLastDay($anyDate)
{
    //$anyDate            =    '2009-08-25';    // date format should be yyyy-mm-dd
    list($yr,$mn,$dt)    =    split('-',$anyDate);    // separate year, month and date
    $timeStamp            =    mktime(0,0,0,$mn,1,$yr);    //Create time stamp of the first day from the give date.
    $firstDay            =     date('D',$timeStamp);    //get first day of the given month
    list($y,$m,$t)        =     split('-',date('Y-m-t',$timeStamp)); //Find the last date of the month and separating it
    $lastDayTimeStamp    =    mktime(0,0,0,$m,$t,$y);//create time stamp of the last date of the give month
    $lastDay            =    date('D',$lastDayTimeStamp);// Find last day of the month
    $arrDay                =    array("$firstDay","$lastDay"); // return the result in an array format.

    return $arrDay;
}
paulsm4
  • 114,292
  • 17
  • 138
  • 190