DateTime::modify

date_modify

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

DateTime::modify -- date_modifyModifie le timestamp

Description

Style orienté objet

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

Style procédural

Modifie le timestamp d'un objet DateTime en l'incrémentant ou le décrémentant dans un format acceptable par strtotime().

Liste de paramètres

object

Style procédural uniquement : Un objet DateTime retourné par la fonction date_create(). Cette fonction modifie cet objet.

modifier

Une chaîne date/heure. Les formats valides sont expliqués dans la documentation sur les formats Date et Heure.

Valeurs de retour

Retourne l'objet modifié DateTime pour chainer les méthodes ou false si une erreur survient.

Erreurs / Exceptions

API Orienté Objet uniquement : Si une chaîne de date ou de temps invalide est fournie, une DateMalformedStringException est lancé.

Historique

Version Description
8.3.0 Lance désormais une DateMalformedStringException avec DateTime::modify() si une chaîne invalide est fourni, au lieu d'un avertissement. date_modify() n'a pas été modifié.

Exemples

Exemple #1 Exemple avec DateTime::modify()

Style orienté objet

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

Style procédural

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

Les exemples ci-dessus vont afficher :

2006-12-13

Exemple #2 Méfiez-vous lors de l'ajout ou la soustraction de mois

<?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";
?>

L'exemple ci-dessus va afficher :

2001-01-31
2001-03-03

Exemple #3 Tous les formats de date et d'heure sont pris en charge

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

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

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

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

L'exemple ci-dessus va afficher :

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

Voir aussi

add a note add a note

User Contributed Notes 15 notes

up
17
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
22
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
9
www dot wesley at gmail dot com
9 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
6
jay dot removethis at grooveshark dot com
11 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
10 years ago
Extension for DateTime class which solves problem of adding or subtracting months

https://gist.github.com/66Ton99/60571ee49bf1906aaa1c
up
3
Jenny jsimonds@atomic jet packs dot com
12 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
hienle
2 years ago
Keep in mind that method chaining is only available from PHP 5.3, so with older versions, this will not work:

<?php
$exp_date
= new DateTime();
echo
$exp_date->modify("-10 month")->format("Y-m-d");
?>

This will work:

<?php
$exp_date
= new DateTime();
$exp_date->modify("-10 month");
echo
$exp_date->format("Y-m-d");
?>
up
0
wes at anonymous dot com
3 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
sg@rg
4 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
0
widowmaker at example dot com
5 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
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
10 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
-1
php at lanar dot com dot au
11 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'.
up
-1
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.
To Top