【问题标题】:Next month, same day in PHP下个月,PHP 的同一天
【发布时间】:2009-06-05 22:02:05
【问题描述】:

我有一个“活动”需要安排在每个月的同一天。

假设您将开始日期设置为 5 月 1 日,您应该在 6 月 1 日、7 月 1 日等获得下一个事件。问题在于 31 日的开始日期(下一个可能是 30 或 28,具体取决于月)。

考虑到根据月份和年份的不同,月份的天数(28、30、31)不同......有什么简单的设置方法?

考虑以下(有缺陷的)下个月函数:

$events = array()

function nextmonth($date) {
   return $date+(60*60*24*30);
}
$curr = $start;
while($curr < $end) {
    $events[ = $curr;
    $curr = nextmonth($curr);
}

编辑添加:对我来说,问题很简单,就是如何解决任何给定月份的天数,从而得到下一个对应的日期。

【问题讨论】:

  • 请更好地格式化代码,谢谢!
  • 我不是 PHP 开发人员,所以只需添加一些上下文,如果指定的日期不是该月的有效日期,Outlook 默认使用该月的最后一天。您需要考虑周末和节假日吗?
  • uk.php.net/cal_days_in_month 或 date('t') - 但我认为实际问题是当用户在 31 日进行活动时该怎么做,这完全取决于事件的内容。
  • 这家伙有一个很好的答案:midwesternmac.com/blogs/jeff-geerling/php-calculating-monthly。不确定它在性能方面有多好,但可以满足您的描述 - 希望它可以帮助找到此页面的任何其他人。

标签: php algorithm datetime


【解决方案1】:

更新:

这将为您提供给定月份的天数:

echo date('t', $timestamp);

见:date()

旧答案:

我不确定您正在考虑的算法,但我相信这会对您有所帮助:

echo date('d-M-y', strtotime('next month'));

【讨论】:

  • strtotime("2009-01-30 +1 月");解析为 2009.03.02 在我的情况下没有用
  • @Guillermo,如果你想将 1 月 31 日映射到 2 月 28 日,那么 1 月 28 日对应的是哪一天?还是 2 月 28 日?
【解决方案2】:

这将返回正确的时间戳,提前 $X 个月:mktime(0,0,0,date("m")+$X,date('d'),date("Y"));

【讨论】:

  • +1 这看起来更自然——拆分日期并在组件中添加内容以获得您想要的内容。
  • +1,我对此进行了测试,当当前月份加上 x 等于大于 12 的数字时,它工作正常。它正确地增加了年份。此外,如果该月中不存在天数,它将移至下个月,并带有额外的天数。
  • 刚刚测试了您的测试用例“2009-01-30”+1 个月,这也返回 2009-3-2
【解决方案3】:

很多时间过去了,但万一有人在搜索这个,在上面的所有答案中,我发现只有@ling answer真正回答了这个问题,但他的答案仍然不是 100% 准确,它输出的年份部分不正确正如您在上面的结果中看到的那样。

这是他修复年份问题的代码的修改版本(我也将其修改为使用 DateTime 类)

  /**
   * Adding month in PHP using DateTime
   * class will render the date in a way that
   * we do not desire, for example adding one month
   * to 2018-01-31 will result in 2018-03-03 but
   * we want it to be 2018-02-28 instead.
   *
   * This method ensure that adding months
   * to a date get caculated properly.
   *
   *
   * @param DateTime $startDate
   * @param int $numberOfMonthsToAdd
   *
   * @return DateTime
   */
  function getSameDayNextMonth(DateTime $startDate, $numberOfMonthsToAdd = 1) {
    $startDateDay = (int) $startDate->format('j');
    $startDateMonth = (int) $startDate->format('n');
    $startDateYear = (int) $startDate->format('Y');

    $numberOfYearsToAdd = floor(($startDateMonth + $numberOfMonthsToAdd) / 12);
    if ((($startDateMonth + $numberOfMonthsToAdd) % 12) === 0) {
      $numberOfYearsToAdd--;
    }
    $year = $startDateYear + $numberOfYearsToAdd;

    $month = ($startDateMonth + $numberOfMonthsToAdd) % 12;
    if ($month === 0) {
      $month = 12;
    }
    $month = sprintf('%02s', $month);

    $numberOfDaysInMonth = (new DateTime("$year-$month-01"))->format('t');
    $day = $startDateDay;
    if ($startDateDay > $numberOfDaysInMonth) {
      $day = $numberOfDaysInMonth;
    }
    $day = sprintf('%02s', $day);

    return new DateTime("$year-$month-$day");
  }


  // Quick Test
  $startDate = new DateTime('2018-01-31');
  for($i=0; $i <= 40; $i++) {
    echo getSameDayNextMonth($startDate, $i)->format('Y-m-d') . "\n";
  }

输出:

2018-01-31
2018-02-28
2018-03-31
2018-04-30
2018-05-31
2018-06-30
2018-07-31
2018-08-31
2018-09-30
2018-10-31
2018-11-30
2018-12-31
2019-01-31
2019-02-28
2019-03-31
2019-04-30
2019-05-31
2019-06-30
2019-07-31
2019-08-31
2019-09-30
2019-10-31
2019-11-30
2019-12-31
2020-01-31
2020-02-29
2020-03-31
2020-04-30
2020-05-31
2020-06-30
2020-07-31
2020-08-31
2020-09-30
2020-10-31
2020-11-30
2020-12-31
2021-01-31
2021-02-28
2021-03-31
2021-04-30
2021-05-31

【讨论】:

    【解决方案4】:

    试试这个。 reday 是补充日期(int - 一个月中的一天)。如果少于今天,它将给出本月的reday,但不会多于没有。这个月的天数。如果超过今天给下个月的reday同样的条件。我用它来计算每月补充点的“下一次补充”。返回是dd/mm/yyyy

    function nextrefill($reday) {
        if (date("j") < $reday) {
            $numd = date("t");
            if ($reday > $numd) {
                return str_pad($numd, 2, "0", STR_PAD_LEFT).date("/m/Y");
            } else {
                return str_pad($reday, 2, "0", STR_PAD_LEFT).date("/m/Y");
            }
        } else {
            $nextm = date('m', strtotime('first day of next month'));
            $nextmy = date('Y', strtotime('first day of next month'));
            $numd = cal_days_in_month(CAL_GREGORIAN, $nextm, $nextmy);
            if ($reday > $numd) {
                return str_pad($numd, 2, "0", STR_PAD_LEFT)."/".$nextm."/".$nextmy;
            } else {
                return str_pad($reday, 2, "0", STR_PAD_LEFT)."/".$nextm."/".$nextmy;
            }
        }
    }
    

    更直接的指向(编辑)

    function nextmonthday($day) {
            $next_month = date('m', strtotime('first day of next month'));
            $year_of_next_month = date('Y', strtotime('first day of next month'));
            $no_of_day_in_next_month = cal_days_in_month(CAL_GREGORIAN, $nextm, $nextmy);
            if ($day > $no_of_day_in_next_month){
                return str_pad($no_of_day_in_next_month, 2, "0", STR_PAD_LEFT)."/".$next_month."/".$year_of_next_month;
            } else {
                return str_pad($day, 2, "0", STR_PAD_LEFT)."/".$next_month."/".$year_of_next_month;
            }
    }
    

    【讨论】:

    • 不确定我理解的解释
    【解决方案5】:

    我建议阅读以下关于 php.net 的评论。 strtotime() (php.net)

    编辑:下一个答案给出了我为那些无法破译那里显示的内容的人发布的链接的“摘要”。

    【讨论】:

    • 看起来很有趣..如果您在此处发布此内容,其他人将有机会通过查看此处获得他的答案..
    【解决方案6】:

    由于月份的大小如此不同,设置下个月的最佳方式不是这样:今天,下个月,除非下个月不存在这一天。

    例子:

    June 5, 2009, next month would be July 5, 2009
    August 31, 2009, next month would be September 30, 2009
    

    或者简单地说,strtotime("+1 month")

    【讨论】:

    • 是的,就是这样。但对我来说,问题是解决任何给定月份的天数是多少。
    • @Guillermo,你不必担心这个。 strtotime("+1 月"),字面意思,负责它。
    • 虽然我是这样,但是: echo date("Y-m-d", strtotime("2009-01-30 +1 month"));解析为 2009.03.02
    • 这是 +1 个月,对于当前您 +1 的月份中的天数!
    【解决方案7】:

    试了试,果然有效

    strtotime('last day next month')
    

    所以:

    $today_next_month = strtotime('this day next month');
    $last_day_next_month = strtotime('last day next month');
    if (date('d', $today_next_month) < date('d', $last_day_next_month)){
        $date = date('m-d-Y', $last_day_next_month); 
    } else {
        $date = date('m-d-Y', $today_next_month);
    }
    echo "And the winner is : $date<br/>";
    

    【讨论】:

    • 我不得不说,我根本没想到这会奏效。但是,它做到了!
    【解决方案8】:

    没有人提到这个替代方案,尽管结果与其他替代方案相同,并且可能不是 OP 正在寻找的:

    $today = new DateTime();
    $today->modify("+1 month");
    echo $today->format("d.m.Y");
    

    为什么不直接存储月份和日期,然后将月份加一,然后使用 PHP 的 checkdate 函数验证日期,将日期减一直到日期有效?

    【讨论】:

      【解决方案9】:

      我知道这是一篇旧帖子,但我想提供一种不同的方法。

      因此,与其尝试找出一个月中的天数(这有点复杂),不如使用当前月份轻松找出下个月,例如:

      date("m", strtotime($current."+1 month"));
      

      然后使用date("d")获取当前月份的日期,并从上面的代码中连接下个月。

      【讨论】:

        【解决方案10】:

        简单

        public function adicionarMesMantendoDia($date, $months, $format = "Y-m-d"){
                $date = \DateTime::createFromFormat($format, $date);
        
                for($i=0;$i < $months; $i++){
                    $date->modify('+ ' . date("t", $date->getTimestamp())  . ' day');
                }
                return $date;
        }
        

        【讨论】:

          【解决方案11】:

          以上都没有处理不存在的日子(例如 2 月 30 日或 4 月 31 日),所以这是我的功能:

          <?php
          function getSameDayNextMonth($time, $numberMonthsToAdd = 1)
          {
              list($year, $month, $day) = explode('-', date("Y-m-d", $time));
          
              // replace the day by one temporarily (just to make sure it exists for any month
              $numberOfYearsToAdd = floor($numberMonthsToAdd / 12);
              $year += $numberOfYearsToAdd;
              $month = ($month + $numberMonthsToAdd) % 12;
              if (0 === $month) {
                  $month = 12;
              }
              $monthFormatted = sprintf('%02s', $month);
              $nbDaysInThisMonth = date("t", strtotime("$year-$monthFormatted-01"));
          
              if ((int)$day > $nbDaysInThisMonth) {
                  $day = $nbDaysInThisMonth;
              }
              $day = sprintf('%02s', $day);
              return strtotime("$year-$monthFormatted-$day");
          }
          
          
          $time = strtotime("2017-10-31");
          for ($i = 0; $i <= 15; $i++) {
              $_time = getSameDayNextMonth($time, $i);
              echo date("Y-m-d", $_time) . '<br>';
          }
          
          /**
           * 2017-10-31
           * 2017-11-30
           * 2017-12-31
           * 2017-01-31
           * 2017-02-28
           * 2017-03-31
           * 2017-04-30
           * 2017-05-31
           * 2017-06-30
           * 2017-07-31
           * 2017-08-31
           * 2017-09-30
           * 2018-10-31
           * 2018-11-30
           * 2018-12-31
           * 2018-01-31
           */
          

          【讨论】:

          • 据我所知,这是唯一真正回答问题的答案,虽然它在年份部分有问题,但我在答案中修复了它。
          【解决方案12】:

          这个功能怎么样:

              function getNextMonthN($date, $n = 1) {
                $newDate = strtotime('+' . $n . ' months', $date);
                if (date('j', $date) !== (date('j', $newDate))) {
                  $newDate = mktime(0, 0, 0, date('n', $newDate), 0, date('Y', $newDate));
                }
                return $newDate;  
              }
          

          在php.net手册站点comments的strtotime条目下还有另一种解决方案。

          【讨论】:

            【解决方案13】:

            我也遇到了同样的问题,但是当我尝试上述解决方案时,它们无法完美地为我工作,我最终尝试并提出了新的解决方案:

            $startDate = date("Y-m-d");
            $month = date("m",strtotime($startDate));
            $nextmonth = date("m",strtotime("$startDate +1 month"));
            if((($nextmonth-$month) > 1) || ($month == 12 && $nextmonth != 1))
            {
                $nextDate = date( 't.m.Y',strtotime("$startDate +1 week"));
            }else
            {
                $nextDate = date("Y-m-d",strtotime("$startDate +1 month"));
            }
            echo $nextSameDate = date("Y",$nextDate).'-'.date("m",$nextDate).'-'.date("d",$startDate);
            

            【讨论】:

              【解决方案14】:

              日期时间 OOP 样式

              <?php
              //Start Date -> 18-09-2015
              $expiry = new DateTime();
              for($i=1;$i<5;$i++){
                 $expiry->modify('+1 Month');
                 echo $expiry->format('d-m-Y');
                 echo '<br>';
              }
              ?>
              
              //18-10-2015
              //18-11-2015
              //18-12-2015
              //18-01-2016
              

              【讨论】:

                【解决方案15】:

                我创建了一个名为 my_strtotime 的函数来处理月份的加减。它基本上是 strtotime 的包装器。

                它检查添加或减去一个月是否会在同一天安全地为您提供所需的月份。如果没有,那么它会安全地完成。

                添加月份时,它会添加 15 以让您安全进入下个月,然后返回该月的最后一天。 减去月份时,取本月的第一天,减去1。

                注意:正如所写,这适用于 +1 个月或 -1 个月

                函数 _my_strtotime($offset, $reference_date){ //$offset 是一个字符串,例如 (+1 月) if(strpos($offset, 'month') === false){ //如果不添加/减去月份则退出 返回 strtotime($offset, $reference_date); } $other_months_date = strtotime ($offset, $reference_date); $day_of_other_month = date('d', $other_months_date); $day_of_reference_month = date('d', $reference_date ); //检查数字日期是否不同。如果这样操纵。 如果($day_of_reference_month != $day_of_other_month){ if(strpos($offset, '-') === false){ // 添加月份 return strtotime('本月最后一天', strtotime('+15天', $reference_date)); } 别的 { // 减去月份 return strtotime('-1 day', strtotime('本月的第一天', $reference_date)); } } 返回 strtotime($offset, $reference_date); }

                测试结果:

                输入=2016-01-31(+1 个月)输出=2016-02-29 //闰年 输入=2017-01-31(+1 个月) 输出=2017-02-28 输入=2018-05-31(+1 个月) 输出=2018-06-30 输入=2018-07-31(-1 个月)输出=2018-06-30 输入=2016-07-22(-1 个月)输出=2016-06-22 输入=2016-07-22(+1 个月) 输出=2016-08-22

                【讨论】:

                  【解决方案16】:

                  这是我用的

                  这是当前月份日期的时间戳

                  $month_timestamp = strtotime(date('Y-m', $create_timestamp));
                  

                  当天

                  $current_day = date('d');
                  

                  这是下个月的同一天,格式为“Y-m-d”

                  $next_month_first = date('Y-m-' . $current_day, strtotime('next month', $month_timestamp));
                  

                  【讨论】:

                  • 可能下个月的这一天不存在,你应该考虑一下
                  猜你喜欢
                  • 2013-12-24
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 2014-09-27
                  • 1970-01-01
                  • 1970-01-01
                  相关资源
                  最近更新 更多