DateTime::modify

date_modify

(PHP 5 >= 5.2.0, PHP 7, PHP 8)

DateTime::modify -- date_modifyAlters the timestamp

Description

Object-oriented style

public DateTime::modify(string $modifier): DateTime|false

Procedural style

Alter the timestamp of a DateTime object by incrementing or decrementing in a format accepted by DateTimeImmutable::__construct().

Parameters

object

Procedural style only: A DateTime object returned by date_create(). The function modifies this object.

modifier

A date/time string. Valid formats are explained in Date and Time Formats.

Return Values

Returns the modified DateTime object for method chaining or false on failure.

Errors/Exceptions

Object Orientated API only: If an invalid Date/Time string is passed, DateMalformedStringException is thrown.

Changelog

Version Description
8.3.0 Now throws DateMalformedStringException with DateTime::modify() if an invalid string is passed, instead of a warning. date_modify() has not been changed.

Examples

Example #1 DateTime::modify() example

Object-oriented style

<?php
$date
= new DateTime('2006-12-12');
$date->modify('+1 day');
echo
$date->format('Y-m-d');
?>

Procedural style

<?php
$date
= date_create('2006-12-12');
date_modify($date, '+1 day');
echo
date_format($date, 'Y-m-d');
?>

The above examples will output:

2006-12-13

Example #2 Beware when adding or subtracting months

<?php
$date
= new DateTime('2000-12-31');

$date->modify('+1 month');
echo
$date->format('Y-m-d') . "\n";

$date->modify('+1 month');
echo
$date->format('Y-m-d') . "\n";
?>

The above example will output:

2001-01-31
2001-03-03

Example #3 All formats of Date and Time are supported

<?php
$date
= new DateTime('2020-12-31');

$date->modify('July 1st, 2023');
echo
$date->format('Y-m-d H:i') . "\n";

$date->modify('Monday next week');
echo
$date->format('Y-m-d H:i') . "\n";

$date->modify('17:30');
echo
$date->format('Y-m-d H:i') . "\n";
?>

The above example will output:

2023-07-01 00:00
2023-07-03 00:00
2023-07-03 17:30

See Also

add a note add a note

User Contributed Notes 14 notes

up
14
mangotonk at gmail dot com
7 years ago
a slightly more compact way of getting the month shift

<?php

     
/**
     * correctly calculates end of months when we shift to a shorter or longer month
     * workaround for http://php.net/manual/en/datetime.add.php#example-2489
     *
     * Makes the assumption that shifting from the 28th Feb +1 month is 31st March
     * Makes the assumption that shifting from the 28th Feb -1 month is 31st Jan
     * Makes the assumption that shifting from the 29,30,31 Jan +1 month is 28th (or 29th) Feb
     *
     *
     * @param DateTime $aDate
     * @param int $months positive or negative
     *
     * @return DateTime new instance - original parameter is unchanged
     */

   
function MonthShifter (DateTime $aDate,$months){
       
$dateA = clone($aDate);
       
$dateB = clone($aDate);
       
$plusMonths = clone($dateA->modify($months . ' Month'));
       
//check whether reversing the month addition gives us the original day back
       
if($dateB != $dateA->modify($months*-1 . ' Month')){
           
$result = $plusMonths->modify('last day of last month');
        } elseif(
$aDate == $dateB->modify('last day of this month')){
           
$result $plusMonths->modify('last day of this month');
        } else {
           
$result = $plusMonths;
        }
        return
$result;
    }

//TEST

$x = new DateTime('2017-01-30');
echo(
$x->format('Y-m-d')." past end of feb, but not dec<br>");
echo(
'b ' . MonthShifter($x,1)->format(('Y-m-d'))."<br>");
echo(
'c ' . MonthShifter($x,-1)->format(('Y-m-d'))."<br>");

$x = new DateTime('2017-01-15');
echo(
"<br>" . $x->format('Y-m-d')." middle of the month <br>");
echo(
'd ' . MonthShifter($x,1)->format(('Y-m-d'))."<br>");
echo(
'e ' . MonthShifter($x,-1)->format(('Y-m-d'))."<br>");

$x = new DateTime('2017-02-28');
echo(
"<br>" . $x->format('Y-m-d')." end of Feb<br>");
echo(
'f ' . MonthShifter($x,1)->format(('Y-m-d'))."<br>");
echo(
'g ' . MonthShifter($x,-1)->format(('Y-m-d'))."<br>");

$x = new DateTime('2017-01-31');
echo(
"<br>" $x->format('Y-m-d')." end of Jan<br>");
echo(
'h ' . MonthShifter($x,1)->format(('Y-m-d'))."<br>");
echo(
'i ' . MonthShifter($x,-1)->format(('Y-m-d'))."<br>");

$x = new DateTime('2017-01-31');
echo(
"<br>" $x->format('Y-m-d')." end of Jan +/- 1 years diff, leap year respected<br>");
echo(
'j ' . MonthShifter($x,13)->format(('Y-m-d'))."<br>");
echo(
'k ' . MonthShifter($x,-11)->format(('Y-m-d'))."<br>");

//returns

2017-01-30 past end of feb, but not dec
b 2017
-02-28
c 2016
-12-30

2017
-01-15 middle of the month
d 2017
-02-15
e 2016
-12-15

2017
-02-28end of Feb
f 2017
-03-31
g 2017
-01-31

2017
-01-31end of Jan
h 2017
-02-28
i 2016
-12-31

2017
-01-31end of Jan +/- 1 years diff, leap year respected
j 2018
-02-28
k 2016
-02-29
up
21
jenspj at msn dot com
12 years ago
These functions makes sure that adding months or years always ends up in the month you would expect.  Works for positive and negative values

<?php
     
      
    $date
=new DateTime();
   
$date->setDate(2008,2,29);
   
    function
addMonths($date,$months){
        
       
$init=clone $date;
       
$modifier=$months.' months';
       
$back_modifier =-$months.' months';
       
       
$date->modify($modifier);
       
$back_to_init= clone $date;
       
$back_to_init->modify($back_modifier);
       
        while(
$init->format('m')!=$back_to_init->format('m')){
       
$date->modify('-1 day')    ;
       
$back_to_init= clone $date;
       
$back_to_init->modify($back_modifier);   
        }
       
       
/*
        if($months<0&&$date->format('m')>$init->format('m'))
        while($date->format('m')-12-$init->format('m')!=$months%12)
        $date->modify('-1 day');
        else
        if($months>0&&$date->format('m')<$init->format('m'))
        while($date->format('m')+12-$init->format('m')!=$months%12)
        $date->modify('-1 day');
        else
        while($date->format('m')-$init->format('m')!=$months%12)
        $date->modify('-1 day');
        */
       
   
}
    
    function
addYears($date,$years){
       
       
$init=clone $date;
       
$modifier=$years.' years';
       
$date->modify($modifier);
       
        while(
$date->format('m')!=$init->format('m'))
       
$date->modify('-1 day');
       
       
    }
   
   
   
   
addMonths($date,-1);
    
addYears($date,3);
   
   
    echo
$date->format('F j,Y');
    

?>
up
7
www dot wesley at gmail dot com
8 years ago
This is an improvement of @jenspj's answer

<?php

$d
= new DateTime('2007-12-31');

function
addMonths($date, $months)
{
   
$years = floor(abs($months / 12));
   
$leap = 29 <= $date->format('d');
   
$m = 12 * (0 <= $months?1:-1);
    for (
$a = 1;$a < $years;++$a) {
       
$date = addMonths($date, $m);
    }
   
$months -= ($a - 1) * $m;
   
   
$init = clone $date;
    if (
0 != $months) {
       
$modifier = $months . ' months';
       
       
$date->modify($modifier);
        if (
$date->format('m') % 12 != (12 + $months + $init->format('m')) % 12) {
           
$day = $date->format('d');
           
$init->modify("-{$day} days");
        }
       
$init->modify($modifier);
    }
   
   
$y = $init->format('Y');
    if (
$leap && ($y % 4) == 0 && ($y % 100) != 0 && 28 == $init->format('d')) {
       
$init->modify('+1 day');
    }
    return
$init;
}

function
addYears($date, $years)
{
    return
addMonths($date, 12 * $years);
}

echo
$d->format('F j,Y') . ' N<br />';
$d = addMonths($d, +1);
echo
$d->format('F j,Y') . ' +1M<br />';
$d = addMonths($d, +1);
echo
$d->format('F j,Y') . ' +1M<br />';
$d = addYears($d, +60);
echo
$d->format('F j,Y') . ' +60Y<br />';
$d = addYears($d, -59);
echo
$d->format('F j,Y') . ' -59Y<br />';
up
7
jay dot removethis at grooveshark dot com
10 years ago
Due to DST and the way DateTime internally handles dates, it's possible to get stuck in a time loop.

For example:

<?php
$dt
= new DateTime('2012-03-11 3:00AM');
echo
$dt->format('YmdH') . "\n";
$dt->modify("-1 hour");
echo
$dt->format('YmdH') . "\n";
$dt->modify("-1 hour");
echo
$dt->format('YmdH') . "\n";
?>

prints out:

2012031103
2012031103
2012031103

if your timezone is set to America/New_York.
up
4
66Ton99
9 years ago
Extension for DateTime class which solves problem of adding or subtracting months

https://gist.github.com/66Ton99/60571ee49bf1906aaa1c
up
1
sg@rg
3 years ago
Made a different fixed version using the month not the day

<?php

function modifyMonth(DateTime $date, string $modificator): DateTime {
   
$destMonth = $date->format('m') + $date->format('Y') * 12 + ($modificator);
   
$date->modify("$modificator months");
    while (
$date->format('m') + $date->format('Y') * 12 > $destMonth)
       
$date->modify('-1 day');
    return
$date;
}
?>

Testing :

<?php
$dates
= [
    [
'2020/02/29', '+1', '2020/03/29'],
    [
'2020/02/29 12:34', '-1', '2020/01/29 12:34'],
    [
'2020/03/29 01:01', '+1', '2020/04/29 01:01'],
    [
'2020/03/29', '-1', '2020/02/29'],
    [
'2020/03/30', '+1', '2020/04/30'],
    [
'2020/03/30 23:58', '-1', '2020/02/29 23:58'],
    [
'2020/03/31', '+1', '2020/04/30'],
    [
'2020/03/31', '-1', '2020/02/29'],
    [
'2020/03/31', '+2', '2020/05/31'],
    [
'2020/03/31 00:01', '-2', '2020/01/31 00:01'],
    [
'2020/03/01', '+1', '2020/04/01'],
    [
'2020/03/01', '-1', '2020/02/01'],
    [
'2020/02/01', '+1', '2020/03/01'],
    [
'2020/02/01', '-1', '2020/01/01'],
    [
'2020/04/01', '+1', '2020/05/01'],
    [
'2020/04/01 22:22', '-1', '2020/03/01 22:22'],
    [
'2020/12/31', '+2', '2021/02/28'],
    [
'2020/01/31', '-2', '2019/11/30'],
];

foreach (
$dates as $date) {
   
$test = new DateTime($date[0]);
   
modifyMonth($test, $date[1]);
    if (
$test != new DateTime($date[2]))
        echo
$date[0] . " " . $date[1] . " != " . $test->format('c') . "\n";
}
?>

Testing prints no error
up
2
Jenny jsimonds@atomic jet packs dot com
11 years ago
Note: This method modifies the object in-place. So if you want to calculate a new date but assign the new value to a different object, this will NOT work:

<?php
$numMinutes
= 25;
$oDateA = new DateTime('2012-01-01 12:00:00');

print
"
Original:<br>
oDateA = 
{$oDateA->format('Y-m-d H-i-s')}<br>
"
;

$oDateB = $oDateA->modify ("+{$numMinutes} minutes");

print
"
plus
{$numMinutes} minutes:<br>
oDateA = 
{$oDateA->format('Y-m-d H-i-s')}<br>
oDateB = 
{$oDateB->format('Y-m-d H-i-s')}<br>
"
;
?>
...produces this:
oDateA = 2012-01-01 12-00-00
plus 25 minutes:
oDateA = 2012-01-01 12-25-00
oDateB = 2012-01-01 12-25-00

Use something like this instead:
<?php
$numMinutes
= 25;
$oDateA = new DateTime('2012-01-01 12:00:00');

print
"
<p>
Original:<br>
oDateA = 
{$oDateA->format('Y-m-d H-i-s')}<br>
"
;

$oDateB = clone $oDateA;
$oDateB->modify ("+{$numMinutes} minutes");

print
"
plus
{$numMinutes} minutes:<br>
oDateA = 
{$oDateA->format('Y-m-d H-i-s')}<br>
oDateB = 
{$oDateB->format('Y-m-d H-i-s')}<br>
"
;
?>

... produces this:
oDateA = 2012-01-01 12-00-00
plus 25 minutes:
oDateA = 2012-01-01 12-00-00
oDateB = 2012-01-01 12-25-00
up
0
wes at anonymous dot com
2 years ago
Beware, the modify function does not treat .5 as a half minute!

echo "start with:           $str\n";
$datetimeObj = new DateTime($str);
$datetimeObj->modify('1 minutes');
echo "add 1 min:            ".$datetimeObj->format('m/d/Y h:i:s A')."\n";
$datetimeObj = new DateTime($str);
$datetimeObj->modify('.5 minutes');
echo "add .5 min:           ".$datetimeObj->format('m/d/Y h:i:s A')."\n";
$datetimeObj->modify('.53453531 minutes');
echo "add .53453531 min:    ".$datetimeObj->format('m/d/Y h:i:s A')."\n";

start with:              05/25/2021 02:53:40 PM
add 1 min:              05/25/2021 02:54:40 PM
add .5 min:             05/25/2021 02:58:40 PM
add .53453531 min: 01/12/2123 03:09:40 AM

.5 minutes actually ends up adding 5 minutes, not a half minute, and .53453531 adds 53453531 minutes.

So, if you have a function that accepts a number for adding minutes to a datetime, you have to check for < 1 and > -1, and then possibly convert that to seconds and do a seconds modification.
up
0
widowmaker at example dot com
4 years ago
date_default_timezone_set('Europe/Amsterdam');

$dt = new DateTime('27 October 2019 00:20:00');
print_r($dt);
$dt->modify('600 min');
print_r($dt);

$dt = new DateTime('27 October 2019 00:20:00');
$dt->add(date_interval_create_from_date_string('600 min'));
print_r($dt);

// produces
DateTime Object
(
    [date] => 2019-10-27 00:20:00.000000
    [timezone_type] => 3
    [timezone] => Europe/Amsterdam
)
DateTime Object
(
    [date] => 2019-10-27 10:20:00.000000
    [timezone_type] => 3
    [timezone] => Europe/Amsterdam
)
DateTime Object
(
    [date] => 2019-10-27 09:20:00.000000
    [timezone_type] => 3
    [timezone] => Europe/Amsterdam
)
up
0
vittorio dot zamparella at gmail dot com
5 years ago
@Anonimous
There is no bug. Especially not any retarded one.

<?
function plusOneMonthTests($dateString, $expectation) {
    $date = new DateTime($dateString);
    echo "[".$date->format('Y-m-d')."] +1 month = [".$date->modify('+1 month')->format('Y-m-d')."] $expectation \n";
}   
plusOneMonthTests('2001-01-01', 'as expected');
plusOneMonthTests('2001-01-27', 'as expected');
plusOneMonthTests('2001-01-28', 'as expected');
plusOneMonthTests('2001-01-29', 'what would you expect?');
plusOneMonthTests('2001-01-30', 'what would you expect?');
plusOneMonthTests('2001-01-31', 'what would you expect?');
?>
Result:
[2001-01-01] +1 month = [2001-02-01] as expected
[2001-01-27] +1 month = [2001-02-27] as expected
[2001-01-28] +1 month = [2001-02-28] as expected
[2001-01-29] +1 month = [2001-03-01] what would you expect? 29 of february??
[2001-01-30] +1 month = [2001-03-02] what would you expect? or 30 of february?
[2001-01-31] +1 month = [2001-03-03] what would you expect?

As with any tool you need to know how to use it.

I think most people are looking for "the same day of the next month" (or any other number or months).

The calendar is twisted, don't blame the library.
up
0
ilyaplot at gmail dot com
6 years ago
function subMonths(\Datetime $dateTime, int $months)
{
    if ($invert = $months < 0) {
        $months *= -1;
    }

    for ($i=0; $i<$months; $i++) {
        $daysOfMonth = cal_days_in_month(CAL_GREGORIAN, $dateTime->format('m'), $dateTime->format('Y'));
        $dateTime->modify(($invert ? '-' : '+') . $daysOfMonth . ' day');
    }
    return $dateTime;
}

$dateTime = new \DateTime('2004-12-31');

echo $dateTime->modify('-3 month')->format('Y-m-d'); // 2004-10-01

echo subMonths($dateTime, -3)->format('Y-m-d'); // 2004-09-30
up
0
sinus at sinpi dot net
7 years ago
A very simple way to ensure we do not cross over month boundaries when adding months is to just go back a few days if the day number got reset:

<?php
function addMonths($date,$months) {
 
$orig_day = $date->format("d");
 
$date->modify("+".$months." months");
  while (
$date->format("d")<$orig_day && $date->format("d")<5) {
   
$date->modify("-1 day");
  }
}

for (
$i=0;$i<5;$i++) {
 
$d = new DateTime("2000-01-10");
 
addmonths($d,$i);
  echo
$d->format("Y-m-d")."<br>";
}
for (
$i=0;$i<5;$i++) {
 
$d = new DateTime("2000-01-31");
 
addmonths($d,$i);
  echo
$d->format("Y-m-d")."<br>";
}
?>

prints:
2000-01-10
2000-02-10
2000-03-10
2000-04-10
2000-05-10
2000-01-31
2000-02-29
2000-03-31
2000-04-30
2000-05-31
up
0
admin at wmfoi dot com dot br
9 years ago
The changelog says: "5.3.0 - Changed the return value on success from NULL to DateTime".

That means that you can't do a Fluid Interface design with it in PHP 5.2.

In other words, this will not work in 5.2:

<?php
$DateTime
=new DateTime();
echo
$DateTime->modify('+1 day')->format('d');
?>
up
0
php at lanar dot com dot au
10 years ago
modify() ignores any timezone information in the data while the DateTime constructor does not.

$dt = new DateTime( '2013-10-26T11:00:00+11:00' )
will create a +11 timezone while
$dt->modify( '2013-10-26T11:00:00+02:00' )
does not change the timezone or the time.

<?php
$dt
= new DateTime( '2013-10-26T15:00:00Australia/Melbourne' ) ;
echo
"\n", $dt->format( "c" ) ;
echo
"\nTimezone '", $dt->getTimezone()->getName() . "'." ;
// modify $dt to 1 am new york which is 3 pm melbourne
$dt->modify( '2013-10-26T01:00:00America/New_York' ) ;
// result is 1 am melbourne time, not 3 pm
echo "\n", $dt->format( "c" ) ;
echo
"\nTimezone '", $dt->getTimezone()->getName() . "'." ;
?>
Output
2013-10-26T15:00:00+11:00
Timezone 'Australia/Melbourne'.
2013-10-26T01:00:00+11:00
Timezone 'Australia/Melbourne'.
To Top