DateTime::modify

date_modify

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

DateTime::modify -- date_modifyИзменение временной метки

Описание

Объектно-ориентированный стиль

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

Процедурный стиль

Изменяет метку времени объекта DateTime путём добавления или вычитания времени в формате, принятом для функции DateTimeImmutable::__construct().

Список параметров

object

Только для процедурного стиля: объект DateTime, который возвращает функция date_create(). Функция изменяет этот объект.

modifier

Строка даты/времени. Объяснение корректных форматов дано в разделе Форматы даты и времени.

Возвращаемые значения

Возвращает модифицированный объект DateTime для применения в цепи методов или false в случае возникновения ошибки.

Ошибки

Только для объектно-ориентированного API: Если передана строка с недопустимой датой/временем, будет выброшено исключение DateMalformedStringException.

Список изменений

Версия Описание
8.3.0 Теперь вместо предупреждения в методе DateTime::modify() выбрасывается исключение DateMalformedStringException, если передана недопустимая строка. Функция date_modify() не была изменена.

Примеры

Пример #1 Пример использования DateTime::modify()

Объектно-ориентированный стиль

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

Процедурный стиль

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

Результат выполнения приведённых примеров:

2006-12-13

Пример #2 Будьте осторожны при добавлении и вычитании месяцев

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

Результат выполнения приведённого примера:

2001-01-31
2001-03-03

Пример #3 Поддерживаются все форматы даты и времени

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

Результат выполнения приведённого примера:

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

Смотрите также

  • strtotime() - Преобразовывает текстовое представление даты на английском языке в метку времени Unix
  • DateTimeImmutable::modify() - Создаёт новый объект с изменённой временной меткой
  • DateTime::add() - Изменяет объект DateTime, добавляя количество дней, месяцев, лет, часов, минут и секунд
  • DateTime::sub() - Вычитает дни, месяцы, годы, часы, минуты и секунды из объекта DateTime
  • DateTime::setDate() - Устанавливает дату
  • DateTime::setISODate() - Устанавливает дату в формате ISO
  • DateTime::setTime() - Устанавливает время
  • DateTime::setTimestamp() - Устанавливает дату и время на основе метки времени Unix
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