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

    date_modify

    (PHP 5 >= 5.2.0, PHP 7)

    修改日期时间对象的值

    说明

    面向对象风格
    publicDateTime::modify(string $modify): DateTime
    过程化风格
    date_modify(DateTime$object,string $modify): DateTime

    修改一个日期时间对象的值。支持strtotime()函数所允许的字符串。

    参数

    $object

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

    $modify

    日期/时间字符串。正确格式的说明详见日期与时间格式。

    返回值

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

    更新日志

    版本说明
    5.3.6支持绝对的日期时间作为变化量,在之前的版本中,仅支持相对变化量。
    5.3.0将返回值从NULL改为DateTime类型。

    范例

    Example #1DateTime::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
    

    增加或者减少月份的时候需要注意

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

    参见

    • strtotime() 将任何字符串的日期时间描述解析为 Unix 时间戳
    • DateTime::add() 给一个 DateTime 对象增加一定量的天,月,年,小时,分钟以及秒。
    • DateTime::sub() 对一个 DateTime 对象减去一定量的日、月、年、小时、分钟和秒。
    • DateTime::setDate() 设置 DateTime 对象的日期
    • DateTime::setISODate() 设置 ISO 日期
    • DateTime::setTime() 设置 DateTime 对象的时间
    • DateTime::setTimestamp() 以 Unix 时间戳的方式设置 DateTime 对象
    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');
       
     
    ?>
    
    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
    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 />';
    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.
    @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.
    Extension for DateTime class which solves problem of adding or subtracting months
    https://gist.github.com/66Ton99/60571ee49bf1906aaa1c
    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
    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
    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'.
    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
    )
    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
    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');
    ?>