PHP 5.3 DateTime для повторяющихся событий - PullRequest
3 голосов
/ 17 ноября 2009

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

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

Допустим, пользователь смотрит на календарь на 31 декабря - это событие должно отображаться там, повторяясь каждые 3 дня, как обычно. Вопрос в том, как рассчитать эти дни в этом месяце?

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

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

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

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

Когда вы идете НАСТОЯЩЕЕ далеко в будущее, например, в 9999 году, эта оценка может быть отклонена на месяц, тогда я должен вычесть много 3-дневных интервалов, чтобы добраться туда, где мне нужно.

Ответы [ 2 ]

9 голосов
/ 18 ноября 2009

Это можно сделать, используя 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" );
}
?>
3 голосов
/ 18 ноября 2009

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...