• 首页
  • vue
  • TypeScript
  • JavaScript
  • scss
  • css3
  • html5
  • php
  • MySQL
  • redis
  • jQuery
  • DateTime::add()

    date_add

    (PHP 5 >= 5.3.0, PHP 7)

    给一个 DateTime 对象增加一定量的天,月,年,小时,分钟以及秒。

    说明

    面向对象风格
    publicDateTime::add(DateInterval$interval): DateTime
    过程化风格
    date_add(DateTime$object,DateInterval$interval): DateTime

    将给定的DateInterval对象加到DateTime对象上。

    参数

    $object

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

    $interval

    DateInterval对象。

    返回值

    返回被修改的 DateTime 对象,或者在失败时返回FALSE.

    范例

    Example #1DateTime::add()例程

    面向对象风格

    <?php
    $date = new DateTime('2000-01-01');
    $date->add(new DateInterval('P10D'));
    echo $date->format('Y-m-d') . "\n";
    ?>
    

    过程化风格

    <?php
    $date = date_create('2000-01-01');
    date_add($date, date_interval_create_from_date_string('10 days'));
    echo date_format($date, 'Y-m-d');
    ?>
    

    以上例程会输出:

    2000-01-11
    

    FurtherDateTime::add()例程

    <?php
    $date = new DateTime('2000-01-01');
    $date->add(new DateInterval('PT10H30S'));
    echo $date->format('Y-m-d H:i:s') . "\n";
    $date = new DateTime('2000-01-01');
    $date->add(new DateInterval('P7Y5M4DT4H3M2S'));
    echo $date->format('Y-m-d H:i:s') . "\n";
    ?>
    

    以上例程会输出:

    2000-01-01 10:00:30
    2007-06-05 04:03:02
    

    当在 DateTime 上加月的时候要注意

    <?php
    $date = new DateTime('2000-12-31');
    $interval = new DateInterval('P1M');
    $date->add($interval);
    echo $date->format('Y-m-d') . "\n";
    $date->add($interval);
    echo $date->format('Y-m-d') . "\n";
    ?>
    

    以上例程会输出:

    2001-01-31
    2001-03-03
    

    注释

    在 PHP 5.2 的版本中,也可以使用DateTime::modify()方法来替代本方法。

    参见

    • DateTime::sub() 对一个 DateTime 对象减去一定量的日、月、年、小时、分钟和秒。
    • DateTime::diff() Returns the difference between two DateTime objects
    • DateTime::modify() 修改日期时间对象的值
    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.)
    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.
    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
    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);
      }
    }
    ?>
    
    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
    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?
    */
    ?>
    
    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"
    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.
    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?
    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