From bc15ec03e6042167e5bc5aebfa3ee97375416a36 Mon Sep 17 00:00:00 2001 From: Andrew McMillan Date: Wed, 7 Mar 2007 12:39:38 +1300 Subject: [PATCH] Many fixes to the RRule handling both for correctness and PHP5. --- inc/RRule.php | 68 +++++++++++-------- inc/test-RRULE.php | 1 + .../regression-suite/831-Spec-RRULE-1.result | 43 +++++++----- 3 files changed, 69 insertions(+), 43 deletions(-) diff --git a/inc/RRule.php b/inc/RRule.php index 757989a2..52f51f52 100644 --- a/inc/RRule.php +++ b/inc/RRule.php @@ -236,12 +236,10 @@ class iCalDate { } if ( ($this->_dd > 28 && $this->_mo == 2) || $this->_dd > 30 ) { - // Ensure the day of month is still reasonable + // Ensure the day of month is still reasonable and coerce to last day of month if needed $dim = $this->DaysInMonth(); if ( $this->_dd > $dim ) { - // We don't need to check for month > 12, since _dd can't be greater than 31 if it was previously valid - $this->_mo++; - $this->_dd -= $dim; + $this->_dd = $dim; } } $this->_EpochFromParts(); @@ -407,9 +405,10 @@ class iCalDate { foreach( $dayrules AS $k => $v ) { $days = $this->MonthDays($first_dow,$days_in_month,$v); foreach( $days AS $k2 => $v2 ) { - $set[$v2] = 1; + $set[$v2] = $v2; } } + asort( $set, SORT_NUMERIC ); return $set; } @@ -425,8 +424,9 @@ class iCalDate { $set = array(); foreach( $dayrules AS $k => $v ) { $v = intval($v); - if ( $v > 0 && $v <= $days_in_month ) $set[$v] = 1; + if ( $v > 0 && $v <= $days_in_month ) $set[$v] = $v; } + asort( $set, SORT_NUMERIC ); return $set; } @@ -445,8 +445,9 @@ class iCalDate { $daynum = $ical_weekdays[$v]; $dd = $this->_dd - $dow + $daynum; if ( $daynum < $this->_wkst ) $dd += 7; - $set[$dd] = 1; + $set[$dd] = $dd; } + asort( $set, SORT_NUMERIC ); return $set; } @@ -458,10 +459,10 @@ class iCalDate { */ function GreaterThan($lesser) { if ( is_object($lesser) ) { - dbg_error_log( "RRule", " Comparing %s with %s", $this->_text, $lesser->_text ); +// dbg_error_log( "RRule", " Comparing %s with %s", $this->_text, $lesser->_text ); return ( $this->_text > $lesser->_text ); } - dbg_error_log( "RRule", " Comparing %s with %s", $this->_text, $lesser ); +// dbg_error_log( "RRule", " Comparing %s with %s", $this->_text, $lesser ); return ( $this->_text > $lesser ); // These sorts of dates are designed that way... } @@ -473,10 +474,10 @@ class iCalDate { */ function LessThan($greater) { if ( is_object($greater) ) { - dbg_error_log( "RRule", " Comparing %s with %s", $this->_text, $greater->_text ); +// dbg_error_log( "RRule", " Comparing %s with %s", $this->_text, $greater->_text ); return ( $this->_text < $greater->_text ); } - dbg_error_log( "RRule", " Comparing %s with %s", $this->_text, $greater ); +// dbg_error_log( "RRule", " Comparing %s with %s", $this->_text, $greater ); return ( $this->_text < $greater ); // These sorts of dates are designed that way... } @@ -492,7 +493,7 @@ class iCalDate { */ function &MonthDays($dow_first, $days_in_month, $dayspec) { global $ical_weekdays; - dbg_error_log( "RRule", " Getting days for '%s'. %d days starting on a %d", $dayspec, $days_in_month, $dow_first ); + dbg_error_log( "RRule", "MonthDays: Getting days for '%s'. %d days starting on a %d", $dayspec, $days_in_month, $dow_first ); $set = array(); preg_match( '/([0-9-]*)(MO|TU|WE|TH|FR|SA|SU)/', $dayspec, $matches); $numeric = intval($matches[1]); @@ -501,7 +502,7 @@ class iCalDate { $first_matching_day = 1 + ($dow - $dow_first); while ( $first_matching_day < 1 ) $first_matching_day += 7; - dbg_error_log( "RRule", " Looking at %d for first match on (%s/%s), %d for numeric", $first_matching_day, $matches[1], $matches[2], $numeric ); + dbg_error_log( "RRule", " MonthDays: Looking at %d for first match on (%s/%s), %d for numeric", $first_matching_day, $matches[1], $matches[2], $numeric ); while( $first_matching_day <= $days_in_month ) { $set[] = $first_matching_day; @@ -516,10 +517,17 @@ class iCalDate { $numeric--; } $answer = $set[$numeric]; - $set = array( $answer ); + $set = array( $answer => $answer ); + } + else { + $answers = $set; + $set = array(); + foreach( $answers AS $k => $v ) { + $set[$v] = $v; + } } - dbg_log_array( "RRule", 'MonthDays', $set, false ); +// dbg_log_array( "RRule", 'MonthDays', $set, false ); return $set; } @@ -534,9 +542,10 @@ class iCalDate { * * @return array The subset which matches. */ - function &ApplyBySetPos($bysplist, &$set) { - dbg_error_log( "RRule", " Applying '%s' to set of %d days", $bysplist, count($set) ); + function &ApplyBySetPos($bysplist, $set) { + dbg_error_log( "RRule", " ApplyBySetPos: Applying set position '%s' to set of %d days", $bysplist, count($set) ); $subset = array(); + sort( $set, SORT_NUMERIC ); $max = count($set); $positions = split( '[^0-9-]', $bysplist ); foreach( $positions AS $k => $v ) { @@ -546,7 +555,7 @@ class iCalDate { else { $v--; } - $subset[] = $set[$v]; + $subset[$set[$v]] = $set[$v]; } return $subset; } @@ -747,13 +756,13 @@ class RRule { $ptr = $this->_current; - dbg_error_log( "RRule", " WithinScope: Processing list of %d days relative to %s", count($relative_days), $base->Render() ); +// dbg_error_log( "RRule", " WithinScope: Processing list of %d days relative to %s", count($relative_days), $base->Render() ); foreach( $relative_days AS $day => $v ) { - dbg_error_log( "RRule", " WithinScope: Testing for day %d", $day ); $test = new iCalDate($base); $days_in_month = $test->DaysInMonth(); +// dbg_error_log( "RRule", " WithinScope: Testing for day %d based on %s, with %d days in month", $day, $test->Render(), $days_in_month ); if ( $day > $days_in_month ) { $test->SetMonthDay($days_in_month); $test->AddDays(1); @@ -778,9 +787,10 @@ class RRule { return $ok_days; } - if ( $this->_current >= 0 && $test->LessThan($this->_dates[$this->_current]) ) continue; + // if ( $this->_current >= 0 && $test->LessThan($this->_dates[$this->_current]) ) continue; if ( !$test->LessThan($this->_first) ) { + dbg_error_log( "RRule", " WithinScope: Looks like %s is within scope", $test->Render() ); $ok_days[$day] = $test; $ptr++; } @@ -815,11 +825,14 @@ class RRule { $next = new iCalDate($this->_dates[$this->_current]); $this->_current++; +/* dbg_error_log( "RRule", " GetNext: Continuing: %s, starting from %s, consider %s, (%d'th)", isset($this->_started) , (isset($next) ? $next->Render() : "not calculated"), (isset($this->_dates[$this->_current]) ? $this->_dates[$this->_current]->Render():"not calculated") , $this->_current ); +*/ + /** * If we have already found some dates we may just be able to return one of those. */ @@ -836,7 +849,7 @@ class RRule { $days = array(); if ( isset($this->_part['WKST']) ) $next->SetWeekStart($this->_part['WKST']); if ( $this->_part['FREQ'] == "MONTHLY" ) { - dbg_error_log( "RRule", " Calculating more dates for MONTHLY rule" ); + dbg_error_log( "RRule", " GetNext: Calculating more dates for MONTHLY rule" ); $limit = 100; do { $limit--; @@ -858,20 +871,20 @@ class RRule { $days = $next->GetMonthByMonthDay($this->_part['BYMONTHDAY']); } else - $days[$next->_dd] = 1; + $days[$next->_dd] = $next->_dd; if ( isset($this->_part['BYSETPOS']) ) { $days = $next->ApplyBySetpos($this->_part['BYSETPOS'], $days); } $days = $this->WithinScope( $next, $days); - dbg_error_log( "RRule", " Found %d days so far for MONTHLY rule", count($days) ); } while( $limit && count($days) < 1 && ! $this->_finished ); + dbg_error_log( "RRule", " GetNext: Found %d days for MONTHLY rule", count($days) ); } else if ( $this->_part['FREQ'] == "DAILY" ) { - dbg_error_log( "RRule", " Calculating more dates for DAILY rule" ); + dbg_error_log( "RRule", " GetNext: Calculating more dates for DAILY rule" ); $limit = 100; do { $limit--; @@ -886,16 +899,17 @@ class RRule { $days = $next->GetWeekByDay($this->_part['BYDAY']); } else - $days[$next->_dd] = 1; + $days[$next->_dd] = $next->_dd; if ( isset($this->_part['BYSETPOS']) ) { $days = $next->ApplyBySetpos($this->_part['BYSETPOS'], $days); } $days = $this->WithinScope( $next, $days); - dbg_error_log( "RRule", " Found %d days so far for DAILY rule", count($days) ); } while( $limit && count($days) < 1 && ! $this->_finished ); + + dbg_error_log( "RRule", " GetNext: Found %d days for DAILY rule", count($days) ); } $ptr = $this->_current; diff --git a/inc/test-RRULE.php b/inc/test-RRULE.php index 795897f5..81eb72d1 100644 --- a/inc/test-RRULE.php +++ b/inc/test-RRULE.php @@ -16,6 +16,7 @@ $tests = array( , "20061101T160000" => "RRULE:FREQ=WEEKLY;COUNT=15;INTERVAL=1;BYDAY=MO,WE,FR" , "20061104T073000" => "RRULE:FREQ=MONTHLY" , "20061117T073000" => "RRULE:FREQ=MONTHLY;BYDAY=1MO,2WE,3FR,-1SU" + , "20061107T103000" => "RRULE:FREQ=MONTHLY;BYDAY=MO,TU,WE,TH,FR" , "20061107T113000" => "RRULE:FREQ=MONTHLY;BYDAY=MO,TU,WE,TH,FR;BYSETPOS=-1" ); diff --git a/testing/tests/regression-suite/831-Spec-RRULE-1.result b/testing/tests/regression-suite/831-Spec-RRULE-1.result index f8175d2e..d77c5c05 100644 --- a/testing/tests/regression-suite/831-Spec-RRULE-1.result +++ b/testing/tests/regression-suite/831-Spec-RRULE-1.result @@ -1,6 +1,6 @@ HTTP/1.1 200 OK Date: Dow, 01 Jan 2000 00:00:00 GMT -Content-Length: 4314 +Content-Length: 5020 Content-Type: text/plain; charset=UTF-8 Testing the RRule Library @@ -38,8 +38,7 @@ Testing the RRule Library 2006-11-01 16:00:00 2006-11-03 16:00:00 2006-11-06 16:00:00 2006-11-08 16:00:00 2006-11-10 16:00:00 2006-11-13 16:00:00 2006-11-15 16:00:00 2006-11-17 16:00:00 2006-11-20 16:00:00 2006-11-22 16:00:00 2006-11-24 16:00:00 2006-11-27 16:00:00 - 2006-10-30 16:00:00 2006-10-31 16:00:00 2006-11-06 16:00:00 2006-11-08 16:00:00 - 2006-11-10 16:00:00 + 2006-11-29 16:00:00 2006-12-01 16:00:00 2006-12-04 16:00:00 20061104T073000 - RRULE:FREQ=MONTHLY @@ -58,23 +57,35 @@ Testing the RRule Library 2006-11-17 07:30:00 2006-11-26 07:30:00 2006-12-04 07:30:00 2006-12-13 07:30:00 2006-12-15 07:30:00 2006-12-31 07:30:00 2007-01-01 07:30:00 2007-01-10 07:30:00 - 2007-01-19 07:30:00 2007-01-28 07:30:00 2007-02-05 07:30:00 2007-02-07 07:30:00 - 2007-02-16 07:30:00 2007-02-25 07:30:00 2007-03-05 07:30:00 2007-03-07 07:30:00 + 2007-01-19 07:30:00 2007-01-28 07:30:00 2007-02-05 07:30:00 2007-02-14 07:30:00 + 2007-02-16 07:30:00 2007-02-25 07:30:00 2007-03-05 07:30:00 2007-03-14 07:30:00 2007-03-16 07:30:00 2007-03-25 07:30:00 2007-04-02 07:30:00 2007-04-11 07:30:00 - 2007-04-20 07:30:00 2007-04-29 07:30:00 2007-04-30 07:30:00 2007-04-30 07:30:00 - 2007-04-30 07:30:00 2007-04-30 07:30:00 2007-04-30 07:30:00 2007-04-30 07:30:00 - 2007-04-30 07:30:00 2007-04-30 07:30:00 2007-04-30 07:30:00 + 2007-04-20 07:30:00 2007-04-29 07:30:00 2007-05-07 07:30:00 2007-05-09 07:30:00 + 2007-05-18 07:30:00 2007-05-27 07:30:00 2007-06-04 07:30:00 2007-06-13 07:30:00 + 2007-06-15 07:30:00 2007-06-24 07:30:00 2007-07-02 07:30:00 + + +20061107T103000 - RRULE:FREQ=MONTHLY;BYDAY=MO,TU,WE,TH,FR + + 2006-11-07 10:30:00 2006-11-08 10:30:00 2006-11-09 10:30:00 2006-11-10 10:30:00 + 2006-11-13 10:30:00 2006-11-14 10:30:00 2006-11-15 10:30:00 2006-11-16 10:30:00 + 2006-11-17 10:30:00 2006-11-20 10:30:00 2006-11-21 10:30:00 2006-11-22 10:30:00 + 2006-11-23 10:30:00 2006-11-24 10:30:00 2006-11-27 10:30:00 2006-11-28 10:30:00 + 2006-11-29 10:30:00 2006-11-30 10:30:00 2006-12-01 10:30:00 2006-12-04 10:30:00 + 2006-12-05 10:30:00 2006-12-06 10:30:00 2006-12-07 10:30:00 2006-12-08 10:30:00 + 2006-12-11 10:30:00 2006-12-12 10:30:00 2006-12-13 10:30:00 2006-12-14 10:30:00 + 2006-12-15 10:30:00 2006-12-18 10:30:00 2006-12-19 10:30:00 20061107T113000 - RRULE:FREQ=MONTHLY;BYDAY=MO,TU,WE,TH,FR;BYSETPOS=-1 - 2006-11-07 11:30:00 2006-11-30 11:30:00 2006-11-30 11:30:00 2006-11-30 11:30:00 - 2006-11-30 11:30:00 2006-11-30 11:30:00 2006-11-30 11:30:00 2006-11-30 11:30:00 - 2006-11-30 11:30:00 2006-11-30 11:30:00 2006-11-30 11:30:00 2006-11-30 11:30:00 - 2006-11-30 11:30:00 2006-11-30 11:30:00 2006-11-30 11:30:00 2006-11-30 11:30:00 - 2006-11-30 11:30:00 2006-11-30 11:30:00 2006-11-30 11:30:00 2006-11-30 11:30:00 - 2006-11-30 11:30:00 2006-11-30 11:30:00 2006-11-30 11:30:00 2006-11-30 11:30:00 - 2006-11-30 11:30:00 2006-11-30 11:30:00 2006-11-30 11:30:00 2006-11-30 11:30:00 - 2006-11-30 11:30:00 2006-11-30 11:30:00 2006-11-30 11:30:00 + 2006-11-30 11:30:00 2006-12-29 11:30:00 2007-01-31 11:30:00 2007-02-28 11:30:00 + 2007-03-30 11:30:00 2007-04-30 11:30:00 2007-05-31 11:30:00 2007-06-29 11:30:00 + 2007-07-31 11:30:00 2007-08-31 11:30:00 2007-09-28 11:30:00 2007-10-31 11:30:00 + 2007-11-30 11:30:00 2007-12-31 11:30:00 2008-01-31 11:30:00 2008-02-29 11:30:00 + 2008-03-31 11:30:00 2008-04-30 11:30:00 2008-05-30 11:30:00 2008-06-30 11:30:00 + 2008-07-31 11:30:00 2008-08-29 11:30:00 2008-09-30 11:30:00 2008-10-31 11:30:00 + 2008-11-28 11:30:00 2008-12-31 11:30:00 2009-01-30 11:30:00 2009-02-27 11:30:00 + 2009-03-31 11:30:00 2009-04-30 11:30:00 2009-05-29 11:30:00