Warning: file_get_contents(/data/phpspider/zhask/data//catemap/1/php/232.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
PHP DateTime::修改加减月份_Php_Datetime_Date - Fatal编程技术网

PHP DateTime::修改加减月份

PHP DateTime::修改加减月份,php,datetime,date,Php,Datetime,Date,我一直在和很多人合作,最近在添加月份时遇到了我认为是个bug的问题。经过一点研究,它似乎不是一个bug,而是按照预期工作。根据发现的文件: 示例#2在添加或 减去月份 有人能证明为什么这不是一个bug吗 此外,是否有人有任何优雅的解决方案来纠正该问题,并使其在一个月后按预期而不是按预期工作?为什么它不是一个bug: 当前的行为是正确的。以下情况在内部发生: +1个月将月数(最初为1)增加1。这使得日期2010-02-31 第二个月(2月)在2010年只有28天,所以PHP通过继续计算2月1日起的

我一直在和很多人合作,最近在添加月份时遇到了我认为是个bug的问题。经过一点研究,它似乎不是一个bug,而是按照预期工作。根据发现的文件:

示例#2在添加或 减去月份

有人能证明为什么这不是一个bug吗

此外,是否有人有任何优雅的解决方案来纠正该问题,并使其在一个月后按预期而不是按预期工作?

为什么它不是一个bug: 当前的行为是正确的。以下情况在内部发生:

  • +1个月
    将月数(最初为1)增加1。这使得日期
    2010-02-31

  • 第二个月(2月)在2010年只有28天,所以PHP通过继续计算2月1日起的天数来自动纠正这一点。然后在3月3日结束

  • 如何获得您想要的: 想要得到你想要的是:手动检查下个月。然后加上下个月的天数

    我希望你能自己编写代码。我只是想知道该怎么做

    PHP 5.3方式: 为了获得正确的行为,您可以使用PHP5.3的一个新功能,该功能引入了相对时间节
    第一天的
    。此节可与
    下个月
    第五个月
    +8个月
    结合使用,以转到指定月份的第一天。您可以使用此代码获得下个月的第一天,而不是从您正在做的事情中获得
    +1个月的时间,如下所示:

    <?php
    $d = new DateTime( '2010-01-31' );
    $d->modify( 'first day of next month' );
    echo $d->format( 'F' ), "\n";
    ?>
    
    
    
    此脚本将正确输出2月份的
    。当PHP处理下个月的第一天时,会发生以下事情:

  • next month
    将月份数(最初为1)增加1。这使得日期为2010-02-31

  • 的第一天
    将天数设置为
    1
    ,从而产生日期2010-02-01


  • 如果您只是想避免跳过一个月,您可以执行类似的操作来获取日期,并在下一个月运行循环,将日期减少一个,然后重新检查直到一个有效日期,其中$starting_COMPUTED是strotime的有效字符串(即mysql datetime或“now”)。这会发现月底是午夜前1分钟,而不是跳过月份

        $start_dt = $starting_calculated;
    
        $next_month = date("m",strtotime("+1 month",strtotime($start_dt)));
        $next_month_year = date("Y",strtotime("+1 month",strtotime($start_dt)));
    
        $date_of_month = date("d",$starting_calculated);
    
        if($date_of_month>28){
            $check_date = false;
            while(!$check_date){
                $check_date = checkdate($next_month,$date_of_month,$next_month_year);
                $date_of_month--;
            }
            $date_of_month++;
            $next_d = $date_of_month;
        }else{
            $next_d = "d";
        }
        $end_dt = date("Y-m-$next_d 23:59:59",strtotime("+1 month"));
    
    $this_month_last_year_end = new \DateTime();
    $this_month_last_year_end->modify('first day of this month');
    $this_month_last_year_end->modify('-1 year');
    $this_month_last_year_end->modify('last day of this month');
    $this_month_last_year_end->setTime(23, 59, 59);
    
    这可能有用:

    echo Date("Y-m-d", strtotime("2013-01-01 +1 Month -1 Day"));
      // 2013-01-31
    
    echo Date("Y-m-d", strtotime("2013-02-01 +1 Month -1 Day"));
      // 2013-02-28
    
    echo Date("Y-m-d", strtotime("2013-03-01 +1 Month -1 Day"));
      // 2013-03-31
    
    echo Date("Y-m-d", strtotime("2013-04-01 +1 Month -1 Day"));
      // 2013-04-30
    
    echo Date("Y-m-d", strtotime("2013-05-01 +1 Month -1 Day"));
      // 2013-05-31
    
    echo Date("Y-m-d", strtotime("2013-06-01 +1 Month -1 Day"));
      // 2013-06-30
    
    echo Date("Y-m-d", strtotime("2013-07-01 +1 Month -1 Day"));
      // 2013-07-31
    
    echo Date("Y-m-d", strtotime("2013-08-01 +1 Month -1 Day"));
      // 2013-08-31
    
    echo Date("Y-m-d", strtotime("2013-09-01 +1 Month -1 Day"));
      // 2013-09-30
    
    echo Date("Y-m-d", strtotime("2013-10-01 +1 Month -1 Day"));
      // 2013-10-31
    
    echo Date("Y-m-d", strtotime("2013-11-01 +1 Month -1 Day"));
      // 2013-11-30
    
    echo Date("Y-m-d", strtotime("2013-12-01 +1 Month -1 Day"));
      // 2013-12-31
    

    我同意OP的观点,即这是违反直觉和令人沮丧的,但在发生这种情况的场景中,确定
    +1个月
    意味着什么也是如此。考虑这些例子:

    您从2015-01-31开始,希望增加一个月6次,以获得发送电子邮件时事通讯的计划周期。考虑到OP最初的期望,这将返回:

    • 2015-01-31
    • 2015-02-28
    • 2015-03-31
    • 2015-04-30
    • 2015-05-31
    • 2015-06-30
    请立即注意,我们希望
    +1个月
    是指
    月的最后一天
    ,或者,每次迭代增加1个月,但始终参考起点。我们可以将其理解为“下个月的第31天或该月内的最后一天”,而不是将其解释为“本月的最后一天”。这意味着我们从4月30日跳到5月31日,而不是5月30日。请注意,这不是因为它是“月的最后一天”,而是因为我们希望“最接近开始月份的日期”

    因此,假设我们的一个用户订阅了另一份时事通讯,从2015-01-30开始。
    +1个月的直观日期是什么?一种解释是“下个月30日或最接近可用日期”,返回:

    • 2015-01-30
    • 2015-02-28
    • 2015-03-30
    • 2015-04-30
    • 2015-05-30
    • 2015-06-30
    这将是好的,除非我们的用户在同一天得到两个时事通讯。让我们假设这是一个供应方问题,而不是需求方问题。我们不担心用户会因为在同一天收到两份新闻稿而感到烦恼,而是我们的邮件服务器负担不起发送两倍新闻稿的带宽。考虑到这一点,我们将“+1个月”的另一种解释返回为“在每个月的第二天到最后一天发送”,该解释将返回:

    • 2015-01-30
    • 2015-02-27
    • 2015-03-30
    • 2015-04-29
    • 2015-05-30
    • 2015-06-29
    现在我们已经避免了与第一组的任何重叠,但我们也以4月和6月29日结束,这当然符合我们最初的直觉,
    +1个月
    只需返回
    m/$d/Y
    ,或者在所有可能的月份返回有吸引力且简单的
    m/30/Y
    。所以现在让我们考虑使用两个日期:<代码> + 1个月< /代码>的第三个解释:

    1月31日
    • 2015-01-31
    • 2015-03-03
    • 2015-03-31
    • 2015-05-01
    • 2015-05-31
    • 2015-07-01
    1月30日
    • 2015-01-30
    • 2015-03-02
    • 2015-03-30
    • 2015-04-30
    • 2015-05-30
    • 2015-06-30
    以上有一些问题。二月被跳过,这可能是一个问题,无论是供应端(比如说,如果有一个月的带宽分配,二月被浪费,三月被加倍)还是需求端(用户觉得二月被骗了,认为额外的三月是为了纠正错误)。另一方面,请注意两个日期集:

    • 永不重叠
    • 当该月有日期时,总是在同一日期(因此1月30日的套装看起来很干净)
    • 在“正确”日期的3天内(大多数情况下为1天)
    • 他们的继任者和前任至少有28天(一个农历月),所以分布非常均匀
    考虑到最后两组数据,如果其中一个数据超出了预期范围,那么简单地将其回滚并不困难
    echo Date("Y-m-d", strtotime("2013-01-01 +1 Month -1 Day"));
      // 2013-01-31
    
    echo Date("Y-m-d", strtotime("2013-02-01 +1 Month -1 Day"));
      // 2013-02-28
    
    echo Date("Y-m-d", strtotime("2013-03-01 +1 Month -1 Day"));
      // 2013-03-31
    
    echo Date("Y-m-d", strtotime("2013-04-01 +1 Month -1 Day"));
      // 2013-04-30
    
    echo Date("Y-m-d", strtotime("2013-05-01 +1 Month -1 Day"));
      // 2013-05-31
    
    echo Date("Y-m-d", strtotime("2013-06-01 +1 Month -1 Day"));
      // 2013-06-30
    
    echo Date("Y-m-d", strtotime("2013-07-01 +1 Month -1 Day"));
      // 2013-07-31
    
    echo Date("Y-m-d", strtotime("2013-08-01 +1 Month -1 Day"));
      // 2013-08-31
    
    echo Date("Y-m-d", strtotime("2013-09-01 +1 Month -1 Day"));
      // 2013-09-30
    
    echo Date("Y-m-d", strtotime("2013-10-01 +1 Month -1 Day"));
      // 2013-10-31
    
    echo Date("Y-m-d", strtotime("2013-11-01 +1 Month -1 Day"));
      // 2013-11-30
    
    echo Date("Y-m-d", strtotime("2013-12-01 +1 Month -1 Day"));
      // 2013-12-31
    
         $date = date('Y-m-d', strtotime("+1 month"));
         echo $date;
    
    foreach(range(0,5) as $count) {
        $new_date = clone $date;
        $new_date->modify("+$count month");
        $expected_month = $count + 1;
        $actual_month = $new_date->format("m");
        if($expected_month != $actual_month) {
            $new_date = clone $date;
            $new_date->modify("+". ($count - 1) . " month");
            $new_date->modify("+4 weeks");
        }
        
        echo "* " . nl2br($new_date->format("Y-m-d") . PHP_EOL);
    }
    
    $time = new DateTime('2014-01-31');
    echo $time->format('d-m-Y H:i') . '<br/>';
    
    $time->add( add_months(1, $time));
    
    echo $time->format('d-m-Y H:i') . '<br/>';
    
    
    
    function add_months( $months, \DateTime $object ) {
        $next = new DateTime($object->format('d-m-Y H:i:s'));
        $next->modify('last day of +'.$months.' month');
    
        if( $object->format('d') > $next->format('d') ) {
            return $object->diff($next);
        } else {
            return new DateInterval('P'.$months.'M');
        }
    }
    
    $startDate = new \DateTime( '2015-08-30' );
    $endDate = clone $startDate;
    
    $billing_count = '6';
    $billing_unit = 'm';
    
    $endDate->add( new \DateInterval( 'P' . $billing_count . strtoupper( $billing_unit ) ) );
    
    if ( intval( $endDate->format( 'n' ) ) > ( intval( $startDate->format( 'n' ) ) + intval( $billing_count ) ) % 12 )
    {
        if ( intval( $startDate->format( 'n' ) ) + intval( $billing_count ) != 12 )
        {
            $endDate->modify( 'last day of -1 month' );
        }
    }
    
                       $datetime = new DateTime("2014-01-31");
                        $month = $datetime->format('n'); //without zeroes
                        $day = $datetime->format('j'); //without zeroes
    
                        if($day == 31){
                            $datetime->modify('last day of next month');
                        }else if($day == 29 || $day == 30){
                            if($month == 1){
                                $datetime->modify('last day of next month');                                
                            }else{
                                $datetime->modify('+1 month');                                
                            }
                        }else{
                            $datetime->modify('+1 month');
                        }
    echo $datetime->format('Y-m-d H:i:s');
    
    date("Y-m-d",strtotime("+1 month",time()));
    
    <?php
    function sameDateNextMonth(DateTime $createdDate, DateTime $currentDate) {
        $addMon = clone $currentDate;
        $addMon->add(new DateInterval("P1M"));
    
        $nextMon = clone $currentDate;
        $nextMon->modify("last day of next month");
    
        if ($addMon->format("n") == $nextMon->format("n")) {
            $recurDay = $createdDate->format("j");
            $daysInMon = $addMon->format("t");
            $currentDay = $currentDate->format("j");
            if ($recurDay > $currentDay && $recurDay <= $daysInMon) {
                $addMon->setDate($addMon->format("Y"), $addMon->format("n"), $recurDay);
            }
            return $addMon;
        } else {
            return $nextMon;
        }
    }
    
    $createdDate = new DateTime("2015-03-31");
    echo "created date = " . $createdDate->format("Y-m-d") . PHP_EOL;
    
    $next = sameDateNextMonth($createdDate, $createdDate);
    echo "   next date = " . $next->format("Y-m-d") . PHP_EOL;
    
    foreach(range(1, 12) as $i) {
        $next = sameDateNextMonth($createdDate, $next);
        echo "   next date = " . $next->format("Y-m-d") . PHP_EOL;
    }
    
    created date = 2015-03-31
       next date = 2015-04-30
       next date = 2015-05-31
       next date = 2015-06-30
       next date = 2015-07-31
       next date = 2015-08-31
       next date = 2015-09-30
       next date = 2015-10-31
       next date = 2015-11-30
       next date = 2015-12-31
       next date = 2016-01-31
       next date = 2016-02-29
       next date = 2016-03-31
       next date = 2016-04-30
    
    /**
     * Adds months without jumping over last days of months
     *
     * @param \DateTime $date
     * @param int $monthsToAdd
     * @return \DateTime
     */
    
    public function addMonths($date, $monthsToAdd) {
        $tmpDate = clone $date;
        $tmpDate->modify('first day of +'.(int) $monthsToAdd.' month');
    
        if($date->format('j') > $tmpDate->format('t')) {
            $daysToAdd = $tmpDate->format('t') - 1;
        }else{
            $daysToAdd = $date->format('j') - 1;
        }
    
        $tmpDate->modify('+ '. $daysToAdd .' days');
    
    
        return $tmpDate;
    }
    
    $dt = new DateTime('2012-01-31');
    
    echo $dt->format('Y-m-d'), PHP_EOL;
    
    $day = $dt->format('j');
    $dt->modify('first day of +1 month');
    $dt->modify('+' . (min($day, $dt->format('t')) - 1) . ' days');
    
    echo $dt->format('Y-m-d'), PHP_EOL;
    
    2012-01-31
    2012-02-29
    
    $this_month_last_year_end = new \DateTime();
    $this_month_last_year_end->modify('first day of this month');
    $this_month_last_year_end->modify('-1 year');
    $this_month_last_year_end->modify('last day of this month');
    $this_month_last_year_end->setTime(23, 59, 59);
    
    $ds = new DateTime();
    $ds->modify('+1 month');
    $ds->modify('first day of this month');
    
    public static function addMonths($monthToAdd, $date) {
        $d1 = new DateTime($date);
    
        $year = $d1->format('Y');
        $month = $d1->format('n');
        $day = $d1->format('d');
    
        if ($monthToAdd > 0) {
            $year += floor($monthToAdd/12);
        } else {
            $year += ceil($monthToAdd/12);
        }
        $monthToAdd = $monthToAdd%12;
        $month += $monthToAdd;
        if($month > 12) {
            $year ++;
            $month -= 12;
        } elseif ($month < 1 ) {
            $year --;
            $month += 12;
        }
    
        if(!checkdate($month, $day, $year)) {
            $d2 = DateTime::createFromFormat('Y-n-j', $year.'-'.$month.'-1');
            $d2->modify('last day of');
        }else {
            $d2 = DateTime::createFromFormat('Y-n-d', $year.'-'.$month.'-'.$day);
        }
        return $d2->format('Y-m-d');
    }
    
    addMonths(-25, '2017-03-31')
    
    '2015-02-28'
    
    $month = 1; $year = 2017;
    echo date('n', mktime(0, 0, 0, $month + 2, -1, $year));
    
    $current_date = new DateTime('now');
    $after_3_months = $current_date->add(\DateInterval::createFromDateString('+3 months'));
    
    $after_3_days = $current_date->add(\DateInterval::createFromDateString('+3 days'));
    
    $start = new DateTimeParsedFromISO8601String('2000-12-31');
    $firstDayOfOneMonthLater = new TheFirstDayOfNMonthsLater($start, 1);
    $firstDayOfTwoMonthsLater = new TheFirstDayOfNMonthsLater($start, 2);
    var_dump($start->value()); // 2000-12-31T00:00:00+00:00
    var_dump($firstDayOfOneMonthLater->value()); // 2001-01-01T00:00:00+00:00
    var_dump($firstDayOfTwoMonthsLater->value()); // 2001-02-01T00:00:00+00:00