开发者

Repeating monthly and yearly events - how to ensure accuracy?

I currently allow for daily or weekly repeating events on my calendar app (using fullCalendar).

My view has a checkbox that activates two dropdowns: one for the repeat interval (daily, weekly) and one for the frequency (once, twice, etc).

$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'); ?>

This eventually reaches my model, which runs a loop checking if the event should repeat -- if so, it will factor in the interval ($row->interval) and the frequency ($i).

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

This works nicely to show multiple daily or开发者_如何学JAVA weekly events based on a single record entry in the database.

The problem is with monthly and yearly. Since these will have variable number of seconds because

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

So how do I resolve this issue? Is there any function or strtotime command that can help indicate precisely that a monthly should repeat on the e.g. 4th or an yearly should repeat on July 4th?

I am using PHP 5.2.14.


Thanks to all that answered - I solved the issue with this function by @akniep from the PHP.net manual on 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;
}

What this does is solve the issue of recurring monthly events on days 29, 30, 31 as well as Feb 28.

I like this function because it solves my problem from a calendar app POV.

If the user requests a monthly recurring event beginning on Jan 31 normal strtotime will behave erratically during February and any other month that does not have 31 days.

As I pointed out in a comment above, Google Calendar resolves this issue somewhat oddly by skipping months in which there's no such date.

I guess it's a matter of preference, but to me it makes better sense to offer placing the recurring event on the last day of the month - so in the Jan 31 example, recurring events will happen on Feb 28 Mar 31 Apr 30 and so on.

I placed this function in a loop that iterates as many recurrences the user requests and it works perfectly. It also works nicely in yearly events, for that I just pass 12 as the number of months.


Seems to work fine with the DateTime class in PHP

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

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

edit: You'll have to give some special consideration as to how you handle monthly recurrence as for 29,30,31 there's a chance they won't appear in the current month. here's an example of an unexpected output:

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

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


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

You should not use the number of seconds if you want to ensure accuracy. If there's a DST change in the time frame, you'll have problems and your calculations won't be accurate. Also since months do not all have the same number of days you can't just continue counting using seconds.

I would advise to use the PHP DateTime class and/or strtotime. The latter understands dates in plain english: strtotime('seconds monday of next month'); returns a timestamp for 13 June.


strtotime can do some amazing things, like "today next month", and "2011-05-09 next week". You can use those to do some basic date adjusments. However, it won't handle this properly in a calendar type situation. If you say "2011-01-30 next month", it won't give you February 30th, it'll give you march 1st/2nd (depending on leap years).

You may have to deconstruct your dates into individual y/m/d/h/m/s components, adjust the repeating component, then reassemble with mktime(), but even this will fix bad dates into equivalent normal ones. In other words, you'll have to do validation with your 'after adjustments' dates to make sure they're valid for your purposes.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜