3

我有一个使用较新的 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 天的间隔才能到达我需要的地方。

4

2 回答 2

9

这可以使用 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" );
}
?>
于 2009-11-18T11:30:53.570 回答
3

您可以将日期格式化为 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 循环将永远无法解决。这还假设您已确保所选日期晚于活动的开始日期。

于 2009-11-17T21:01:05.430 回答