DateTime::add

date_add

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

DateTime::add -- date_add 修改 DateTime 对象,增加天、月、年、小时、分钟以及秒的数量。

说明

面向对象风格

public DateTime::add(DateInterval $interval): DateTime

过程化风格

date_add(DateTime $object, DateInterval $interval): DateTime

将指定的 DateInterval 对象到 DateTime 对象。

DateTimeImmutable::add()一样,但适用于 DateTime

过程化版本将 DateTime 对象作为它的第一个参数。

参数

object

仅过程化风格:由 date_create() 返回的 DateTime 类型的对象。此函数会修改这个对象。

interval

DateInterval 对象。

返回值

返回方法链修改后的 DateTime 对象。

参见

add a note add a note

User Contributed Notes 12 notes

up
192
Anonymous
13 years ago
Note that the add() and sub() methods will modify the value of the object you're calling the method on! This is very untypical for a method that returns a value of its own type. You could misunderstand it that the method would return a new instance with the modified value, but in fact it modifies itself! This is undocumented here. (Only a side note on procedural style mentions it, but it obviously does not apply to object oriented style.)
up
46
Angelo
8 years ago
Another simple solution to adding a month but not autocorrecting days to the next month is this.
(Also works for substracting months)

$dt = new DateTime("2016-01-31");

$oldDay = $dt->format("d");
$dt->add(new DateInterval("P1M")); // 2016-03-02
$newDay = $dt->format("d");

if($oldDay != $newDay) {
    // Check if the day is changed, if so we skipped to the next month.
    // Substract days to go back to the last day of previous month.
    $dt->sub(new DateInterval("P" . $newDay . "D"));
}

echo $dt->format("Y-m-d"); // 2016-02-29

Hope this helps someone.
up
32
Anthony
9 years ago
If you're using PHP >= 5.5, instead of using "glavic at gmail dot com"'s DateTimeEnhanced class, use the built in DateTimeImmutable type. When you call DateTimeImmutable::add() it will return a new object, rather than modifying the original
up
18
patrick dot mckay7 at gmail dot com
9 years ago
Here is a solution to adding months when you want 2014-10-31 to become 2014-11-30 instead of 2014-12-01.

<?php

/**
* Class MyDateTime
*
* Extends DateTime to include a sensible addMonth method.
*
* This class provides a method that will increment the month, and
* if the day is greater than the last day in the new month, it
* changes the day to the last day of that month. For example,
* If you add one month to 2014-10-31 using DateTime::add, the
* result is 2014-12-01. Using MyDateTime::addMonth the result is
* 2014-11-30.
*/
class MyDateTime extends DateTime
{

    public function
addMonth($num = 1)
    {
       
$date = $this->format('Y-n-j');
        list(
$y, $m, $d) = explode('-', $date);

       
$m += $num;
        while (
$m > 12)
        {
           
$m -= 12;
           
$y++;
        }

       
$last_day = date('t', strtotime("$y-$m-1"));
        if (
$d > $last_day)
        {
           
$d = $last_day;
        }

       
$this->setDate($y, $m, $d);
    }

}

?>
up
15
glavic at gmail dot com
11 years ago
If you need add() and sub() that don't modify object values, you can create new methods like this:

<?php

class DateTimeEnhanced extends DateTime {

    public function
returnAdd(DateInterval $interval)
    {
       
$dt = clone $this;
       
$dt->add($interval);
        return
$dt;
    }
   
    public function
returnSub(DateInterval $interval)
    {
       
$dt = clone $this;
       
$dt->sub($interval);
        return
$dt;
    }

}

$interval = DateInterval::createfromdatestring('+1 day');

$dt = new DateTimeEnhanced; # initialize new object
echo $dt->format(DateTime::W3C) . "\n"; # 2013-09-12T15:01:44+02:00

$dt->add($interval); # this modifies the object values
echo $dt->format(DateTime::W3C) . "\n"; # 2013-09-13T15:01:44+02:00

$dtNew = $dt->returnAdd($interval); # this returns the new modified object and doesn't change original object
echo $dt->format(DateTime::W3C) . "\n"; # 2013-09-13T15:01:44+02:00
echo $dtNew->format(DateTime::W3C) . "\n"; # 2013-09-14T15:01:44+02:00
up
1
rnealxp at yahoo dot com
7 years ago
What you can do with this function/method is a great example of the philosophy: "just because you can do it doesn't mean you should". I'm talking about two issues: (1) the number of days in the month which varies from months 1-12 as well as for month 2 which could be leap year (or not); and then issue (2): what if there is the need to specify a large quantity of an interval such that it needs to be re-characterized into broader-scoped intervals (i.e. 184 seconds ==> 3 minutes-4 seconds). Examples in notes elsewhere in the docs for this function illustrate both issues and their undesired effects so I won't focus on them further. But how did I decide to handle? I've gone with four "public" functions and a single "private" function, and without giving you a bunch of code to study, here are their summaries...

1. function adjustYear(int $yearsAdj){ //you can pass in +/- value and I adjust year value by that value but then I also call PHP's 'cal_days_in_month' function to ensure the day number I have in my date does not exceed days in the month for the new year/month combo--if it does, I adjust the day value downward.

2. function adjustMonth(int $monthsAdj){ //same notes as above apply; but also, I allow any number to be passed in for $monthsAdj. I use the 'int' function (int($monthsAdj/12)) and modulus % operator to determine how to adjust both year and month. And again, I use 'cal_days_in_month' function to tweak the day number as needed.

3. function addTime(int $days, int $hours, int $minutes, int $seconds){
// I use date_add and create a DateInterval object from the corresponding string spec (created from the args passed to this function). Note that months and years are excluded due to the bad side-effects already mentioned elsewhere.

4. function subtractTime(int $days, int $hours, int $minutes, int $seconds){
//notes for "addTime" also apply to this function but note that I like separate add and subtract functions because setting the DateInterval property flag to indicate add/subtract is not as intuitive for future coding.

5. function recharacterizeIntervals(int $days, int $hours, int $minutes, int $seconds){ // I convert excessively large quantities of any one interval into the next largest interval using the 'int' function and modulus (%) operator. I then use the result of this function when creating the string interval specification that gets passed when generating the DateInterval object for calling the date_add function (or object-method equivalent).

**Results/goals...
--any number of days/hours/minutes/seconds can be passed in to add/subtractTime and all of "Y/M/D/H/M/S" values get adjusted as you would expect.
--using adjustYear/Month lets you pass +/- values and only "Y/M" values get modified without having undesirable effects on day values.
--a call to the "recharacterize" function helps ensure proper and desired values are in the intervals prior to calling date_add to let it do its work.
up
4
artaxerxes2 at iname dot com
11 years ago
Be careful that the internal timer to your DateTime object can be changed drastically when adding even 1 second, during the switch from DST to normal.
Consider the following:
<?php

$ts
= 1383458399; /* 2013-11-03 01:59:59 in Eastern Saving Time */
$dst = DateTime::createFromFormat('U',$ts, new DateTimeZone('GMT')); /* timezone is ignored for a unix timestamp, but if we don't put it, php throws warnings */
$dst->setTimeZone(new DateTimeZone('EST5EDT')); /* a timezone effectuating the change */
$second = new DateInterval('PT1S'); /* one second */

echo $ts . "\t" . $dst->format("U\tY-m-d H:i:s T") . "\n";

$dst->add($second);
$ts++;

echo
$ts . "\t" . $dst->format("U\tY-m-d H:i:s T") . "\n";

/* results:
1383458399    1383458399    2013-11-03 01:59:59 EDT
1383458400    1383462000    2013-11-03 02:00:00 EST

noticed how the second column went from 1383458399 to 1383462000 even though only 1 second was added?
*/

?>
up
1
skysnake
3 years ago
$TodaySQL = substr(date(DATE_ISO8601 ),0,10)
$LastYearSQL = date('Y.m.d',strtotime("-1 years"))
$NextMonthEndSQL = date('Y.m.d',strtotime("+1 months"))

// handy little SQL date formats

//Today
2021-03-24
//Last year
2020.03.24
//Next month
2021.04.24
up
0
info at mobger dot de
3 years ago
Remark, that calculations on date are not defined as bijective operations.  The Summertime is integrated by mixing two concepts. You should test it beforehead.

Datetime will correct a date after each summation, if a date (29.2.2021 => 1.3.2021) or a datetime (29.3.2020 2:30 am (Europe/Berlin) => 29.3.2020 3:30 or 29.3.2020 1:30)

Example
<?php

$expectEaster
= date_create_from_format('Y-m-d H:i:s', '2020-04-12 12:00:00', new DateTimeZone('Europe/Berlin'));
$interval = new DateInterval('PT20761M');
$expectEaster->sub($interval);
echo(
'recalc '.$expectEaster->format('Y-m-d H:i:s')."\n");
$expectEaster->add($interval);
echo(
'easter '.$expectEaster->format('Y-m-d H:i:s')."\n" );

$expectEaster = date_create_from_format('Y-m-d H:i:s', '2020-04-12 12:00:00', new DateTimeZone('Europe/Berlin'));
$interval = new DateInterval('PT20760M');
$expectEaster->sub($interval);
echo(
'recalc '.$expectEaster->format('Y-m-d H:i:s')."\n");
$expectEaster->add($interval);
echo(
'easter '.$expectEaster->format('Y-m-d H:i:s')."\n");

$expectEaster = date_create_from_format('Y-m-d H:i:s', '2020-04-12 12:00:00', new DateTimeZone('Europe/Berlin'));
$interval = new DateInterval('PT20701M');
$expectEaster->sub($interval);
echo(
'recalc '.$expectEaster->format('Y-m-d H:i:s')."\n");
$expectEaster->add($interval);
echo(
'easter '.$expectEaster->format('Y-m-d H:i:s')."\n");

$expectEaster = date_create_from_format('Y-m-d H:i:s', '2020-04-12 12:00:00', new DateTimeZone('Europe/Berlin'));
$interval = new DateInterval('PT20700M');
$expectEaster->sub($interval);
echo(
'recalc '.$expectEaster->format('Y-m-d H:i:s')."\n");
$expectEaster->add($interval);
echo(
'easter '.$expectEaster->format('Y-m-d H:i:s')."\n");

// Result
// recalc 2020-03-29 00:59:00  // reduce the missing hour before you calcuclate the datetime
// easter 2020-04-12 11:00:00  // recalcultate the date and remove the missing hour
// recalc 2020-03-29 03:00:00  //because 2020-03-29 3:00:00 [it means 2020-03-29 2:00:00] does not exist add 60 min)
// easter 2020-04-12 13:00:00 
// recalc 2020-03-29 03:59:00 // -(12*60+(11+2)*1440+21*60) = -(20701 min) =  = 29.3.2020 2:59(not exist => no-equivalent add of one hour) =>  29.3.2020 3:59
// easter 2020-04-12 13:00:00 // Recalc add 60 minutes, because the hour does not exist.)
// recalc 2020-03-29 03:00:00 // -(12*60+(11+2)*1440+21*60 min)= -(20700 min) = 29.3.2020 3:00
// easter 2020-04-12 12:00:00 // +(12*60+(11+2)*1440+21*60 min)= +(20700 min) = 29.3.2020
up
-1
Binho RbSoft
6 years ago
If you use fraction of seconds, you may have surprises. It only occurs when the sum of the floating point parts results in exactly 1 second (0.5 + 0.5 ou 0.3 + 0.7, for example). See these cases at intervals slightly bigger than 1 second:

<?php
$objDataHora
= date_create("2017-12-31T23:59:59.300");
$objIntervalo = new DateInterval("PT0S");
$objIntervalo->f = 0.600;
$objDataHora = date_add($objDataHora, $objIntervalo);
$strDataHora = date_format($objDataHora, "Y-m-d\TH:i:s.v");
?>

$strDataHora is correct: "2017-12-31T23:59:59.900"

<?php
$objDataHora
= date_create("2017-12-31T23:59:59.300");
$objIntervalo = new DateInterval("PT0S");
$objIntervalo->f = 0.800;
$objDataHora = date_add($objDataHora, $objIntervalo);
$strDataHora = date_format($objDataHora, "Y-m-d\TH:i:s.v");
?>

$strDataHora is correct: "2018-01-01T00:00:00.100"

But...

<?php
$objDataHora
= date_create("2017-12-31T23:59:59.300");
$objIntervalo = new DateInterval("PT0S");
$objIntervalo->f = 0.700;
$objDataHora = date_add($objDataHora, $objIntervalo);
$strDataHora = date_format($objDataHora, "Y-m-d\TH:i:s.v");
?>

$strDataHora has "2017-12-31T23:59:59.1000"

To resolve, add 1 second to the interval and f property must be negative (-1.0 plus original value):

<?php
$objDataHora
= date_create("2017-12-31T23:59:59.300");
$objIntervalo = new DateInterval("PT1S");
$objIntervalo->f = -0.300; // = -1.0 + 0.700
$objDataHora = date_add($objDataHora, $objIntervalo);
$strDataHora = date_format($objDataHora, "Y-m-d\TH:i:s.v");
?>

$strDataHora is correct: "2018-01-01T00:00:00.000"
up
-6
dm at resource-ps dot co dot uk
8 years ago
Be careful when using this function, I may have happened upon a bug in PHP7.

My code is as follows

//get date from post or else fill with today's date
if (isset($_POST["from"]))
{
$from = date_create($_POST["from"]);
}else{
$from = date_create(date("Y-m-d"));
}

//get date from post if there isn't one just take the same date as what is in the $from variable and add one day to it
if (isset($_POST["to"]))
{
$to = date_create($_POST["to"]);
}else{
    $to = $from;
date_modify($to, '+1 day');
}
echo(date_format($from, 'Y-m-d') . " " . date_format($to, 'Y-m-d'));

The resultant output is
$from = 2015-12-11
$to = 2015-12-11

In actuality the result should be
$from = 2015-12-10
$to = 2015-12-11

For some reason the code above modifies the $from variable in the line date_modify($to, '+1 day'); even though it shouldn't as the $from variable isn't being modified.

to fix this i needed to change the code to

//get date from post or else fill with today's date
if (isset($_POST["from"]))
{
$from = date_create($_POST["from"]);
}else{
$from = date_create(date("Y-m-d"));
}

//get date from post if there isn't one just take the same date as what is in the $from variable and add one day to it
if (isset($_POST["to"]))
{
$to = date_create($_POST["to"]);
}else{
    $to = date_create(date("Y-m-d"));
date_modify($to, '+1 day');
}
echo(date_format($from, 'Y-m-d') . " " . date_format($to, 'Y-m-d'));

This isn't strictly the code I wanted. Possible bug?
up
-21
fortruth at mabang dot net
14 years ago
adding 15 min to a datetime

<?php
$initDate
= new DateTime("2010/08/24");

$initDate->add(new DateInterval("PT15M"));
echo
$initDate->format("Y/m/d m:i:s");//result: 2010/08/24 08:15:00
?>

period:
P1Y2M3DT1H2M3S

period time:
PT1H2M3S
To Top