【问题标题】:PHP: DatePeriod with last day of monthPHP:DatePeriod 与本月的最后一天
【发布时间】:2019-05-29 00:32:45
【问题描述】:

我想使用 PHP DateInterval 来迭代几个月:

$from = new DateTime();
$from->setDate(2014, 1, 31);

$period = new DatePeriod($from, new DateInterval('P1M'), 12);

我希望它返回 1 月 31 日、2 月 28 日(因为 DateInterval 是 1 个月),但它实际上返回 1 月 31 日、3 月 3 日、4 月 3 日......因此跳过了 2 月。

有没有办法简单地做到这一点?

谢谢!

编辑:作为参考,这是一个似乎涵盖大多数用例的解决方案:

$date = new DateTime('2014-01-31');
$start = $date->format('n');

for ($i = 0; $i < 28; $i++) {
  $current = clone $date;
  $current->modify('+'.$i.' month');

  if ($current->format('n') > ($start % 12) && $start !== 12) {
    $current->modify('last day of last month');
  }

  $start++;

  echo $current->format('Y-m-d').PHP_EOL;
}

【问题讨论】:

    标签: php datetime


    【解决方案1】:

    你可以使用DateTime::modify():

    $date = new DateTime('last day of january');
    echo $date->format('Y-m-d').PHP_EOL;
    
    for ($i = 1; $i < 12; $i++) {
      $date->modify('last day of next month');
      echo $date->format('Y-m-d').PHP_EOL;
    }
    

    编辑:我想我没有清楚地理解你的问题。这是一个新版本:

    $date = new DateTime('2014-01-31');
    
    for ($i = 0; $i < 12; $i++) {
      $current = clone $date;
      $current->modify('+'.$i.' month');
    
      if ($current->format('n') > $i + 1) {
        $current->modify('last day of last month');
      }
    
      echo $current->format('Y-m-d').PHP_EOL;
    }
    

    【讨论】:

    • 这仅在您需要当月的最后一天时才有效。实际上,我想迭代并使其在所有情况下都有效(例如,从 1 月 2 日开始,它也应该通过每月迭代来工作)。
    • 我明白了。用新代码更新了我的答案。你能测试一下,让我知道这是否是你想要的吗?
    • 是的,我在 2 月份尝试了各种边缘案例,似乎效果很好! (尽管我仍然发现 DatePeriod 的行为不合逻辑......)。非常感谢
    • 其实也有一些失败的情况:如果你把12个月改成24个月,从2015年开始就失败了;)。我会尝试看看如何解决这个问题。
    • 其实很简单,只需要模12:if ($current->format('n') > ($i % 12) + 1。但现在在其他情况下它会失败(例如,如果你不在一月份开始)。
    【解决方案2】:

    你可以这样尝试:

    $date = new DateTime();
    $lastDayOfMonth = $date->modify(
      sprintf('+%d days', $date->format('t') - $date->format('j'))
    );
    

    【讨论】:

      【解决方案3】:

      我可能会这样做

      $max = array (
      31,28,31,30,31,30,31,31,30,31,30,31
      ); //days in month
      
      $month = 1;
      $year = 2014;
      $day = 31;
      
      $iterate = 12;
      $dates = array();
      for ($i = 0;$i < $iterate;$i++) {
          $tmp_month = ($month + $i) % 12;
          $tmp_year = $year + floor($month+$i)/12;
          $tmp_day = min($day, $max[$tmp_month]);
          $tmp = new DateTime();
          $tmp->setDate($tmp_year, $tmp_month + 1, $tmp_day);
          $dates[] = $tmp;
      }
      
      var_dump($dates);
      

      如果可能的话,这会保持每个月的同一天

      【讨论】:

      • 这似乎是“不太糟糕”的解决方案。虽然我担心它需要一些调整(比如考虑到 2 月份有 29 天的年份)。
      【解决方案4】:

      问题是由范围内每个月的最后一天之间的差异引起的。 IE。 2 月结束于 28 而不是 31 并且从最后一天开始增加 1 个月 2014-01-31 + 1 month = 2014-03-03 https://3v4l.org/Y42QJ

      要解决DatePeriod 的问题并稍微简化它,请通过使用first day of this month 将指定日期重置为指定月份的第一天来调整初始日期。

      然后在迭代过程中,您可以使用last day of this month 来修改日期周期日期,以检索当前迭代月份的范围。

      示例:https://3v4l.org/889mB

      $from = new DateTime('2014-01-31');
      $from->modify('first day of this month'); //2014-01-01
      
      $period = new DatePeriod($from, new DateInterval('P1M'), 12);
      foreach ($period as $date) {
          echo $date->modify('last day of this month')->format('Y-m-d');
      }
      

      结果:

      2014-01-31
      2014-02-28
      2014-03-31
      2014-04-30
      2014-05-31
      2014-06-30
      2014-07-31
      2014-08-31
      2014-09-30
      2014-10-31
      2014-11-30
      2014-12-31
      2015-01-31
      

      然后对这种方法进行扩展,以便从指定日期检索所需的日期,例如29th。当该日期超出该月的范围时,您可以提取指定的日期并根据需要调整当前迭代的月份。

      示例:https://3v4l.org/SlEJc

      $from = new DateTime('2014-01-29');
      $day = $from->format('j');
      $from->modify('first day of this month'); //2014-01-01
      
      $period = new DatePeriod($from, new DateInterval('P1M'), 12);
      foreach ($period as $date) {
          $lastDay = clone $date;
          $lastDay->modify('last day of this month');
          $date->setDate($date->format('Y'), $date->format('n'), $day);
          if ($date > $lastDay) {
             $date = $lastDay;
          }
          echo $date->format('Y-m-d');
      }
      

      结果:

      2014-01-29
      2014-02-28 #notice the last day of february is used
      2014-03-29
      2014-04-29
      2014-05-29
      2014-06-29
      2014-07-29
      2014-08-29
      2014-09-29
      2014-10-29
      2014-11-29
      2014-12-29
      2015-01-29
      

      【讨论】:

        猜你喜欢
        • 2021-10-30
        • 2017-09-04
        • 1970-01-01
        • 1970-01-01
        • 2012-02-13
        • 1970-01-01
        • 2021-12-24
        • 2012-12-16
        • 2021-12-24
        相关资源
        最近更新 更多