重复每月和每年的活动 - 如何确保准确性?

时间:2011-05-09 20:16:30

标签: php calendar strtotime recurring-events

我目前允许在我的日历应用上进行每日或每周重复活动(使用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。

4 个答案:

答案 0 :(得分:5)

感谢所有回答 - 我通过PHP.net manual on strtotime() @akniep 解决了此功能的问题。

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开始的每月定期活动,则会在2月份以及任何其他没有31天的月份内表现不正常。

正如我在上面的评论中指出的那样,谷歌日历通过跳过没有这样日期的月份来解决这个问题。

我想这是一个偏好问题,但对我来说,提供在一个月的最后一天定期发生的事件更有意义 - 所以在Jan 31示例中,重复发生的事件将发生在{{1 }} Feb 28 Mar 31等等。

我将此函数放在一个循环中,该循环迭代用户请求的重复次数并且它完美地工作。它也适用于年度活动,因为我只是将Apr 30作为月数传递。

答案 1 :(得分:3)

似乎可以正常使用PHP中的DateTime

$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;

答案 2 :(得分:2)

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

如果要确保准确性,则不应使用秒数。如果在时间范围内有DST更改,您将遇到问题,并且您的计算将不准确。此外,由于几个月都没有相同的天数,你不能只用秒来继续计数。

我建议使用PHP DateTime类和/或strtotime。后者用简单的英语理解日期:strtotime('seconds monday of next month');返回6月13日的时间戳。

答案 3 :(得分:1)

strtotime可以做一些惊人的事情,比如“今天下个月”和“2011-05-09下周”。您可以使用它们进行一些基本的日期调整。但是,在日历类型的情况下,它无法正确处理。如果你说“2011-01-30下个月”,它将不会给你2月30日,它会给你3月1日/ 2日(取决于闰年)。

您可能需要将日期解构为单独的y / m / d / h / m / s组件,调整重复组件,然后使用mktime()重组,但即使这样也会将错误日期修复为等效的正常日期。换句话说,您必须对“调整后”日期进行验证,以确保它们符合您的目的。