Fix handling of BYMONTHDAY=-N in repeat rules.

It seems PHP's date::setDate function doesn't do what we want when
you hand it a negative integer so we need to override a little more
of it's behaviour.  We have to make sure that date::modify is not
called with a days greater than the month we might land in when we
add a number of months to it.
This commit is contained in:
Andrew McMillan 2011-09-28 05:21:31 +08:00
parent 11892c4e97
commit e64f92ff86
3 changed files with 65 additions and 3 deletions

View File

@ -343,6 +343,9 @@ class RepeatRuleDateTime extends DateTime {
// printf( "Modify '%s' by: >>%s<<\n", $this->__toString(), $interval );
// print_r($this);
if ( !isset($interval) || $interval == '' ) $interval = '1 day';
if ( parent::format('d') > 28 && strstr($interval,'month') !== false ) {
$this->setDate(null,null,28);
}
parent::modify($interval);
return $this->__toString();
}
@ -431,10 +434,36 @@ class RepeatRuleDateTime extends DateTime {
}
/**
* Returns a 1 if this year is a leap year, otherwise a 0
* @param int $year The year we are quizzical about.
* @return 1 if this is a leap year, 0 otherwise
*/
public static function hasLeapDay($year) {
if ( ($year % 4) == 0 && (($year % 100) != 0 || ($year % 400) == 0) ) return 1;
return 0;
}
/**
* Returns the number of days in a year/month pair
* @param int $year
* @param int $month
* @return int the number of days in the month
*/
public static function daysInMonth( $year, $month ) {
if ($month == 4 || $month == 6 || $month == 9 || $month == 11) return 30;
else if ($month != 2) return 31;
return 28 + RepeatRuleDateTime::hasLeapDay($year);
}
function setDate( $year=null, $month=null, $day=null ) {
if ( !isset($year) ) $year = parent::format('Y');
if ( !isset($month) ) $month = parent::format('m');
if ( !isset($day) ) $day = parent::format('d');
if ( $day < 0 ) {
$day += RepeatRuleDateTime::daysInMonth($year, $month) + 1;
}
parent::setDate( $year , $month , $day );
return $this;
}
@ -749,7 +778,9 @@ class RepeatRule {
$this->current_set = array();
foreach( $instances AS $k => $instance ) {
foreach( $this->bymonthday AS $k => $monthday ) {
$this->current_set[] = $this->date_mask( clone($instance), null, null, $monthday, null, null, null);
$expanded = $this->date_mask( clone($instance), null, null, $monthday, null, null, null);
if ( DEBUG_RRULE ) printf( "Expanded BYMONTHDAY $monthday into date %s from %s\n", $expanded->format('c'), $instance->format('c') );
$this->current_set[] = $expanded;
}
}
}

View File

@ -80,7 +80,8 @@ $tests = array(
, new RRuleTest( "The last working day of each month", "20061107T113000", "RRULE:FREQ=MONTHLY;BYDAY=MO,TU,WE,TH,FR;BYSETPOS=-1;COUNT=30" )
, new RRuleTest( "Every working day", "20081020T103000", "RRULE:FREQ=MONTHLY;BYDAY=MO,TU,WE,TH,FR;COUNT=30" )
, new RRuleTest( "Every working day", "20081020T110000", "RRULE:FREQ=DAILY;INTERVAL=1;BYDAY=MO,TU,WE,TH,FR;COUNT=30" )
//**SQL is wrong , new RRuleTest( "1st Tuesday, 2nd Wednesday, 3rd Thursday & 4th Friday, every March, June, September, October and December", "20081001T133000", "RRULE:FREQ=MONTHLY;INTERVAL=1;BYDAY=1TU,2WE,3TH,4FR;BYMONTH=3,6,9,10,12" )
, new RRuleTest( "The last day of each month", "20110831", "RRULE:FREQ=MONTHLY;BYMONTHDAY=-1" )
, new RRuleTest( "1st Tuesday, 2nd Wednesday, 3rd Thursday & 4th Friday, every March, June, September, October and December (SQL is wrong)", "20081001T133000", "RRULE:FREQ=MONTHLY;INTERVAL=1;BYDAY=1TU,2WE,3TH,4FR;BYMONTH=3,6,9,10,12" )
, new RRuleTest( "Every tuesday and friday", "20081017T084500", "RRULE:FREQ=MONTHLY;INTERVAL=1;BYDAY=TU,FR;COUNT=30" )
, new RRuleTest( "Every tuesday and friday", "20081017T084500", "RRULE:FREQ=WEEKLY;INTERVAL=1;BYDAY=TU,FR;COUNT=30" )
, new RRuleTest( "Every tuesday and friday", "20081017T084500", "RRULE:FREQ=DAILY;INTERVAL=1;BYDAY=TU,FR;COUNT=30" )

View File

@ -2,7 +2,7 @@ HTTP/1.1 200 OK
Date: Dow, 01 Jan 2000 00:00:00 GMT
DAV: 1, 2, 3, access-control, calendar-access, calendar-schedule
DAV: extended-mkcol, calendar-proxy, bind, addressbook, calendar-auto-schedule
Content-Length: 5323
Content-Length: 7224
Content-Type: text/plain
#!/usr/bin/php
@ -51,6 +51,36 @@ PHP & SQL results are identical (-:
20081020T110000 - RRULE:FREQ=DAILY;INTERVAL=1;BYDAY=MO,TU,WE,TH,FR;COUNT=30
Every working day
PHP & SQL results are identical (-:
=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=
20110831 - RRULE:FREQ=MONTHLY;BYMONTHDAY=-1
The last day of each month
PHP & SQL results are identical (-:
=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=
20081001T133000 - RRULE:FREQ=MONTHLY;INTERVAL=1;BYDAY=1TU,2WE,3TH,4FR;BYMONTH=3,6,9,10,12
1st Tuesday, 2nd Wednesday, 3rd Thursday & 4th Friday, every March, June, September, October and December (SQL is wrong)
PHP & SQL results differ :-(
PHP Result:
2008-10-07 13:30:00 2008-10-08 13:30:00 2008-10-16 13:30:00 2008-10-24 13:30:00
2008-12-02 13:30:00 2008-12-10 13:30:00 2008-12-18 13:30:00 2008-12-26 13:30:00
2009-03-03 13:30:00 2009-03-11 13:30:00 2009-03-19 13:30:00 2009-03-27 13:30:00
2009-06-02 13:30:00 2009-06-10 13:30:00 2009-06-18 13:30:00 2009-06-26 13:30:00
2009-09-01 13:30:00 2009-09-09 13:30:00 2009-09-17 13:30:00 2009-09-25 13:30:00
2009-10-06 13:30:00 2009-10-14 13:30:00 2009-10-15 13:30:00 2009-10-23 13:30:00
2009-12-01 13:30:00 2009-12-09 13:30:00 2009-12-17 13:30:00 2009-12-25 13:30:00
2010-03-02 13:30:00 2010-03-10 13:30:00
SQL Result:
2008-10-07 13:30:00 2008-10-08 13:30:00 2008-10-16 13:30:00 2008-10-24 13:30:00
2008-11-04 13:30:00 2008-11-12 13:30:00 2008-11-20 13:30:00 2008-11-28 13:30:00
2008-12-02 13:30:00 2008-12-10 13:30:00 2008-12-18 13:30:00 2008-12-26 13:30:00
2009-01-06 13:30:00 2009-01-14 13:30:00 2009-01-15 13:30:00 2009-01-23 13:30:00
2009-02-03 13:30:00 2009-02-11 13:30:00 2009-02-19 13:30:00 2009-02-27 13:30:00
2009-03-03 13:30:00 2009-03-11 13:30:00 2009-03-19 13:30:00 2009-03-27 13:30:00
2009-04-07 13:30:00 2009-04-08 13:30:00 2009-04-16 13:30:00 2009-04-24 13:30:00
2009-05-05 13:30:00 2009-05-13 13:30:00
=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=
20081017T084500 - RRULE:FREQ=MONTHLY;INTERVAL=1;BYDAY=TU,FR;COUNT=30
Every tuesday and friday