【问题标题】:PHP 5.3 DateTime for recurring events用于重复事件的 PHP 5.3 DateTime
【发布时间】:2010-12-17 14:34:06
【问题描述】:

我有一个使用较新的 PHP DateTime 类的日历应用程序。我有一种处理重复事件的方法,但它看起来很老套,我想看看你们是否有更好的想法:

  1. 我有一个从 2009 年 11 月 16 日(2009 年 11 月 16 日)开始的周期性活动
  2. 每 3 天发生一次
  3. 该事件将无限期重复

假设用户查看 3100 年 12 月的日历 - 该事件应该显示在那里,每 3 天重复一次,就像平常一样。问题是 - 我如何计算该月的那些天数?

===========================================

我基本上就是这样做的,但我知道我错过了一些更容易的东西:

  1. 我计算正在查看的月初(3100 年 12 月 1 日)和存储为 $daysDiff 的事件开始日期(2009 年 11 月 16 日)之间的天数差异
  2. 我减去模数,因此从一开始就得到 3 天的因子,如下所示:$daysDiff - ($daysDiff % 3)
  3. 为了争论,假设给我 3100 年 11 月 29 日作为日期。
  4. 然后我在该日期上重复添加 3 天,直到所有日期都在 3100 年 12 月内

我的主要问题来自第 1 步。PHP DateInterval::date_diff 函数不计算天数差异。它会给我几年、几个月和几天的时间。然后,我不得不捏造这些数字,以获得 3100 年 12 月左右的估计日期。 2009 年 11 月 16 日 +(1090 年 * 365.25 天)+(9 个月 * 30.5 天)+ 15 天

当你真正深入到 9999 年这样的未来时,这个估计可能会相差一个月,然后我必须减去很多 3 天的间隔才能到达我需要的地方。

【问题讨论】:

    标签: php datetime recurring


    【解决方案1】:

    这可以使用 DatePeriod 作为迭代器很好地完成,然后过滤到您要显示的开始日期:

    <?php
    class DateFilterIterator extends FilterIterator {
        private $starttime;
        public function __construct(Traversable $inner, DateTime $start) {
            parent::__construct(new IteratorIterator($inner));
            $this->starttime = $start->getTimestamp();
        }
        public function accept() {
            return ($this->starttime < $this->current()->getTimestamp());
        }
    }
    
    $db = new DateTime( '2009-11-16' );
    $de = new DateTime( '2020-12-31 23:59:59' );
    $di = DateInterval::createFromDateString( '+3 days' );
    $df = new DateFilterIterator(
        new DatePeriod( $db, $di, $de ),
        new DateTime( '2009-12-01') );
    
    foreach ( $df as $dt )
    {
        echo $dt->format( "l Y-m-d H:i:s\n" );
    }
    ?>
    

    【讨论】:

      【解决方案2】:

      您可以将日期格式化为 unix 时间戳,然后使用模除法查找所选月份中的第一个实例,然后从那里以 3 天为增量步进。所以对于你的例子:

      $startDate = new DateTime(20091116);
      $startTimestamp = $startDate->format('u');
      $recursEvery = 259200; // 60*60*24*3 = seconds in 3 days
      
      // find the first occurrence in the selected month (September)
      $calendarDate = new DateTime(31000901); // init to Sept 1, 3100
      while (0 != (($calendarDate->format('u') - $startTimestamp) % $recursEvery)) {
          $calendarDate->modify('+1 day');
      }
      
      $effectiveDates = array();
      while ($calendarDate->format('m') == 9) {
          $effectiveDates[] = clone $calendarDate;
          $calendarDate->modify('+3 day');
      }
      
      //$effectiveDates is an array of every date the event occurs in September, 3100.
      

      显然,您需要换出一些变量,以便用户可以选择任何月份,但这应该是基本算法。此外,请确保您的 DateTimes 是正确的日期,但时间设置为 00:00:00,否则第一个 while 循环将永远无法解决。这还假设您已确保所选日期晚于活动的开始日期。

      【讨论】:

      • ...当然,除非您以 64 位运行,否则 unix 时间戳不会在 3100 年起作用。
      • 嗨 Keith - 我喜欢你的想法,但不幸的是,这对我来说也是一个限制(在 32 位上)。似乎无法完成这些日期的直接“数学”,请查看我上面的“捏造”方式
      • 如果您在时间戳前面放置一个“@”,如下所示: new DateTime('@'.$timestamp);哇,没有意识到这是 09 年的最后一次活动......对不起。
      猜你喜欢
      • 2010-11-25
      • 2012-11-16
      • 2011-01-22
      • 1970-01-01
      • 1970-01-01
      • 2012-02-08
      • 2013-06-09
      • 1970-01-01
      • 2012-04-14
      相关资源
      最近更新 更多