Повторение ежемесячных и ежегодных событий - как обеспечить точность?

В настоящее время я разрешаю ежедневные или еженедельные повторяющиеся события в моем приложении календаря (используя fullCalendar).

В моем представлении есть флажок, который активирует два раскрывающихся списка: один для интервала повтора (ежедневно, еженедельно) и один для частоты (один раз, дважды и т. Д.).

$evt_interval_options = array( //this will be $row->interval in the model
    '86400' => 'Daily',   
    '604800' => 'Weekly'; 

$evt_frequency_options = array(  //this will be //$i in the model
    '1' => '2',    
    '2' => '3',    

<?php echo form_checkbox('evt_repeat', 'FALSE', FALSE, 'class="repeat_checkbox"'); 
      echo form_dropdown('evt_interval', $evt_interval_options');
      echo form_dropdown('evt_frequency', $evt_frequency_options'); ?>

В конечном итоге это доходит до моей модели, которая запускает цикл, проверяющий, должно ли событие повторяться - если да, то учитываются интервал ($row->interval) и частота ($i).

$cal_data[] = array(
    'start' => strtotime($row->date_1 . ' ' . $row->time_1) + ($i ? ($row->interval * $i) : 0),
);

Это удобно для отображения нескольких ежедневных или еженедельных событий на основе одной записи в базе данных.

Проблема с ежемесячными и ежегодными. Поскольку у них будет переменное количество секунд, потому что

03/01/2011 00:00:00 --> 04/01/2011 00:00:00 ===> 2674800 seconds
04/01/2011 00:00:00 --> 05/01/2011 00:00:00 ===> 2592000 seconds
and so on for monthly and yearly differences

Итак, как мне решить эту проблему? Есть ли какая-либо функция или команда strtotime, которая может помочь точно указать, что раз в месяц следует повторять, например, 4th или годовой должен повторяться July 4th?

Я использую PHP 5.2.14.


person pepe    schedule 09.05.2011    source источник


Ответы (4)


Благодаря всем ответам - я решил проблему с этой функцией с помощью @akniep из Руководство PHP.net по strtotime ().

function get_x_months_to_the_future( $base_time = null, $months = 1 )
{
    if (is_null($base_time))
        $base_time = time();

    $x_months_to_the_future    = strtotime( "+" . $months . " months", $base_time );

    $month_before              = (int) date( "m", $base_time ) + 12 * (int) date( "Y", $base_time );
    $month_after               = (int) date( "m", $x_months_to_the_future ) + 12 * (int) date( "Y", $x_months_to_the_future );

    if ($month_after > $months + $month_before)
        $x_months_to_the_future = strtotime( date("Ym01His", $x_months_to_the_future) . " -1 day" );

    return $x_months_to_the_future;
}

Это решает проблему повторяющихся ежемесячных событий в дни 29, 30, 31, а также Feb 28.

Мне нравится эта функция, потому что она решает мою проблему из приложения календаря POV.

Если пользователь запрашивает ежемесячное повторяющееся событие, начинающееся Jan 31, нормальный strtotime будет вести себя хаотично в течение февраля и любого другого месяца, в котором нет 31 дня.

Как я указывал в комментарии выше, Календарь Google решает эту проблему несколько странным образом, пропуская месяцы, в которых такой даты нет.

Я предполагаю, что это вопрос предпочтений, но для меня имеет смысл предложить размещение повторяющегося события в последний день месяца - поэтому в примере Jan 31 повторяющиеся события будут происходить в Feb 28 Mar 31 Apr 30 и так далее.

Я поместил эту функцию в цикл, который повторяет столько повторений, сколько пользователь запрашивает, и она отлично работает. Это также хорошо работает с ежегодными мероприятиями, для этого я просто указываю 12 как количество месяцев.

person pepe    schedule 10.05.2011

Кажется, отлично работает с классом DateTime в PHP

$dt = new DateTime('3rd Jan 2011');
echo $dt->format('r') . PHP_EOL;

$dt->modify('+1 year');
echo $dt->format('r') . PHP_EOL;

изменить: вам нужно будет уделить особое внимание тому, как вы обрабатываете ежемесячное повторение, поскольку для 29,30,31 есть вероятность, что они не появятся в текущем месяце. вот пример неожиданного вывода:

$dt = new DateTime('29 Jan 2011');
echo $dt->format('r') . PHP_EOL;

$dt->modify('+1 month');
echo $dt->format('r') . PHP_EOL;
person James C    schedule 09.05.2011
comment
Это интересный момент - если вы поместите событие в Календарь Google, скажем, на 31 января 2011 года и попросите его повторять ежемесячно 3 раза, оно пропустит февраль и апрель. - person pepe; 10.05.2011

$evt_interval_options = array( //this will be $row->interval in the model
    '86400' => 'Daily',   
    '604800' => 'Weekly';

Вы не должны использовать количество секунд, если хотите обеспечить точность. Если во временных рамках произойдет переход на летнее время, у вас возникнут проблемы, и ваши расчеты будут неточными. Кроме того, поскольку не все месяцы имеют одинаковое количество дней, вы не можете просто продолжать считать, используя секунды.

Я бы посоветовал использовать класс PHP DateTime и / или strtotime. Последний понимает даты на простом английском языке: strtotime('seconds monday of next month'); возвращает отметку времени за 13 июня.

person Arjan    schedule 09.05.2011

strtotime может делать удивительные вещи, например «сегодня в следующем месяце» и «2011-05-09 на следующей неделе». Вы можете использовать их для базовой настройки даты. Однако он не справится с этим должным образом в ситуации календарного типа. Если вы скажете «2011-01-30 в следующем месяце», он не даст вам 30 февраля, он даст вам 1/2 марта (в зависимости от високосных лет).

Возможно, вам придется деконструировать свои даты на отдельные компоненты y / m / d / h / m / s, настроить повторяющийся компонент, а затем повторно собрать с помощью mktime (), но даже это исправит плохие даты в эквивалентные нормальные. Другими словами, вам нужно будет провести валидацию с датами «после корректировок», чтобы убедиться, что они действительны для ваших целей.

person Marc B    schedule 09.05.2011