PHP 5.3 DateTime для повторяющихся событий

У меня есть приложение календаря, которое использует новые классы PHP DateTime. У меня есть способ, которым я обрабатываю повторяющиеся события, но, похоже, это хак-иш, и я хотел посмотреть, есть ли у вас, ребята, лучшие идеи:

  1. У меня повторяющееся событие, которое начинается 16.11.2009 (16 ноября 2009 г.)
  2. Это произойдет каждые 3 дня
  3. Событие будет повторяться бесконечно

Предположим, что пользователь смотрит календарь на декабрь 3100 года – это событие должно показать, что он повторяется каждые 3 дня, как обычно. Вопрос в том, как мне рассчитать эти дни в этом месяце?

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

Вот как я в основном это делаю, но я знаю, что мне не хватает чего-то проще:

  1. Я рассчитываю разницу в днях между началом месяца (1 декабря 3100) и датой начала события (16 ноября 2009 г.), хранящейся как $ daysDiff
  2. Я вычитаю модуль, так что я получаю коэффициент в 3 дня с самого начала следующим образом: $ daysDiff – ($ daysDiff% 3)
  3. Ради аргументов можно сказать, что дает мне 29 ноября 3100 года как дату.
  4. Затем я добавляю 3 дня до этой даты, пока у меня не будет всех дат в течение 31 декабря

Моя основная проблема связана с шагом 1. Функция PHP DateInterval :: date_diff не вычисляет разницу в днях. Это даст мне годы, месяцы и дни. Затем мне приходится выдумывать цифры, чтобы получить дату оценки около 31 декабря. 11/16/2009 + (1090 лет * 365,25 дня) + (9 месяцев * 30,5 дней) + 15 дней

Когда вы идете в реальность далеко в будущее, как в 9999 году, эта оценка может быть отключена на месяц, тогда мне нужно вычесть много трехдневных интервалов, чтобы добраться туда, где мне нужно.

Вы можете отформатировать дату как временную метку 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 никогда не будет разрешен. Это также предполагает, что вы выбрали выбранную дату позже даты начала события.

Это можно сделать красиво, используя DatePeriod в качестве Iterator, а затем filterd до даты начала, которую вы хотите отобразить:

 <?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 Ymd H:i:s\n" ); } ?>