mirror of
https://gitlab.com/davical-project/davical.git
synced 2026-01-27 00:33:34 +00:00
New RepeatRule - mostly working.
This commit is contained in:
parent
d2d6bf137a
commit
3035e0c1b6
191
inc/RRule-v2.php
191
inc/RRule-v2.php
@ -26,6 +26,8 @@ $rrule_expand_limit = array(
|
||||
'byday' => 'limit', 'byhour' => 'limit', 'byminute' => 'limit', 'bysecond' => 'limit' ),
|
||||
);
|
||||
|
||||
$rrule_day_numbers = array( 'SU' => 0, 'MO' => 1, 'TU' => 2, 'WE' => 3, 'TH' => 4, 'FR' => 5, 'SA' => 6 );
|
||||
|
||||
$GLOBALS['debug_rrule'] = false;
|
||||
// $GLOBALS['debug_rrule'] = true;
|
||||
|
||||
@ -146,7 +148,9 @@ class RepeatRule {
|
||||
|
||||
if ( $this->finished ) return;
|
||||
$got_more = false;
|
||||
while( !$this->finished && !$got_more ) {
|
||||
$loop_limit = 10;
|
||||
$loops = 0;
|
||||
while( !$this->finished && !$got_more && $loops++ < $loop_limit ) {
|
||||
if ( !isset($this->current_base) ) {
|
||||
$this->current_base = clone($this->base);
|
||||
}
|
||||
@ -157,6 +161,7 @@ class RepeatRule {
|
||||
$this->current_set = array( clone($this->current_base) );
|
||||
foreach( $rrule_expand_limit[$this->freq] AS $bytype => $action ) {
|
||||
if ( isset($this->{$bytype}) ) $this->{$action.'_'.$bytype}();
|
||||
if ( !isset($this->current_set[0]) ) break;
|
||||
}
|
||||
sort($this->current_set);
|
||||
if ( isset($this->bysetpos) ) $this->limit_bysetpos();
|
||||
@ -206,7 +211,9 @@ class RepeatRule {
|
||||
$this->current_set = array();
|
||||
foreach( $instances AS $k => $instance ) {
|
||||
foreach( $this->bymonth AS $k => $month ) {
|
||||
$this->current_set[] = $this->date_mask( clone($instance), null, $month, null, null, null, null);
|
||||
$expanded = $this->date_mask( clone($instance), null, $month, null, null, null, null);
|
||||
if ( $GLOBALS['debug_rrule'] ) printf( "Expanded BYMONTH $month into date %s\n", $expanded->format('c') );
|
||||
$this->current_set[] = $expanded;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -221,71 +228,139 @@ class RepeatRule {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private function expand_byday_in_week( $day_in_week ) {
|
||||
global $rrule_day_numbers;
|
||||
|
||||
/**
|
||||
* @TODO: This should really allow for WKST, since if we start a series
|
||||
* on (eg.) TH and interval > 1, a MO, TU, FR repeat will not be in the
|
||||
* same week with this code.
|
||||
*/
|
||||
$dow_of_instance = $day_in_week->format('w'); // 0 == Sunday
|
||||
foreach( $this->byday AS $k => $weekday ) {
|
||||
$dow = $rrule_day_numbers[$weekday];
|
||||
$offset = $dow - $dow_of_instance;
|
||||
if ( $offset < 0 ) $offset += 7;
|
||||
$expanded = clone($day_in_week);
|
||||
$expanded->modify( sprintf('+%d day', $offset) );
|
||||
$this->current_set[] = $expanded;
|
||||
if ( $GLOBALS['debug_rrule'] ) printf( "Expanded BYDAY(W) $weekday into date %s\n", $expanded->format('c') );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private function expand_byday_in_month( $day_in_month ) {
|
||||
global $rrule_day_numbers;
|
||||
|
||||
$first_of_month = $this->date_mask( clone($day_in_month), null, null, 1, null, null, null);
|
||||
$dow_of_first = $first_of_month->format('w'); // 0 == Sunday
|
||||
$days_in_month = cal_days_in_month(CAL_GREGORIAN, $first_of_month->format('m'), $first_of_month->format('Y'));
|
||||
foreach( $this->byday AS $k => $weekday ) {
|
||||
if ( preg_match('{([+-])?(\d)?(MO|TU|WE|TH|FR|SA|SU)}', $weekday, $matches ) ) {
|
||||
$dow = $rrule_day_numbers[$matches[3]];
|
||||
$first_dom = 1 + $dow - $dow_of_first; if ( $first_dom < 1 ) $first_dom +=7; // e.g. 1st=WE, dow=MO => 1+1-3=-1 => MO is 6th, etc.
|
||||
$whichweek = intval($matches[2]);
|
||||
if ( $GLOBALS['debug_rrule'] ) printf( "Expanding BYDAY(M) $weekday in month of %s\n", $instance->format('c') );
|
||||
if ( $whichweek > 0 ) {
|
||||
$whichweek--;
|
||||
$monthday = $first_dom;
|
||||
if ( $matches[1] == '-' ) {
|
||||
$monthday += 35;
|
||||
while( $monthday > $days_in_month ) $monthday -= 7;
|
||||
$monthday -= (7 * $whichweek);
|
||||
}
|
||||
else {
|
||||
$monthday += (7 * $whichweek);
|
||||
}
|
||||
if ( $monthday > 0 && $monthday <= $days_in_month ) {
|
||||
$expanded = $this->date_mask( clone($day_in_month), null, null, $monthday, null, null, null);
|
||||
if ( $GLOBALS['debug_rrule'] ) printf( "Expanded BYDAY(M) $weekday now $monthday into date %s\n", $expanded->format('c') );
|
||||
$this->current_set[] = $expanded;
|
||||
}
|
||||
}
|
||||
else {
|
||||
for( $monthday = $first_dom; $monthday <= $days_in_month; $monthday += 7 ) {
|
||||
$expanded = $this->date_mask( clone($day_in_month), null, null, $monthday, null, null, null);
|
||||
if ( $GLOBALS['debug_rrule'] ) printf( "Expanded BYDAY(M) $weekday now $monthday into date %s\n", $expanded->format('c') );
|
||||
$this->current_set[] = $expanded;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private function expand_byday_in_year( $day_in_year ) {
|
||||
global $rrule_day_numbers;
|
||||
|
||||
$first_of_year = $this->date_mask( clone($day_in_year), null, 1, 1, null, null, null);
|
||||
$dow_of_first = $first_of_year->format('w'); // 0 == Sunday
|
||||
$days_in_year = 337 + cal_days_in_month(CAL_GREGORIAN, 2, $first_of_year->format('Y'));
|
||||
foreach( $this->byday AS $k => $weekday ) {
|
||||
if ( preg_match('{([+-])?(\d)?(MO|TU|WE|TH|FR|SA|SU)}', $weekday, $matches ) ) {
|
||||
$expanded = clone($first_of_year);
|
||||
$dow = $rrule_day_numbers[$matches[3]];
|
||||
$first_doy = 1 + $dow - $dow_of_first; if ( $first_doy < 1 ) $first_doy +=7; // e.g. 1st=WE, dow=MO => 1+1-3=-1 => MO is 6th, etc.
|
||||
$whichweek = intval($matches[2]);
|
||||
if ( $GLOBALS['debug_rrule'] ) printf( "Expanding BYDAY(Y) $weekday from date %s\n", $instance->format('c') );
|
||||
if ( $whichweek > 0 ) {
|
||||
$whichweek--;
|
||||
$yearday = $first_doy;
|
||||
if ( $matches[1] == '-' ) {
|
||||
$yearday += 371;
|
||||
while( $yearday > $days_in_year ) $yearday -= 7;
|
||||
$yearday -= (7 * $whichweek);
|
||||
}
|
||||
else {
|
||||
$yearday += (7 * $whichweek);
|
||||
}
|
||||
if ( $yearday > 0 && $yearday <= $days_in_year ) {
|
||||
$expanded->modify(sprintf('+%d day', $yearday - 1));
|
||||
if ( $GLOBALS['debug_rrule'] ) printf( "Expanded BYDAY(Y) $weekday now $yearday into date %s\n", $expanded->format('c') );
|
||||
$this->current_set[] = $expanded;
|
||||
}
|
||||
}
|
||||
else {
|
||||
$expanded->modify(sprintf('+%d day', $first_doy - 1));
|
||||
for( $yearday = $first_doy; $yearday <= $days_in_year; $yearday += 7 ) {
|
||||
if ( $GLOBALS['debug_rrule'] ) printf( "Expanded BYDAY(Y) $weekday now $yearday into date %s\n", $expanded->format('c') );
|
||||
$this->current_set[] = clone($expanded);
|
||||
$expanded->modify('+1 week');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private function expand_byday() {
|
||||
if ( $this->freq == 'MONTHLY' ) {
|
||||
if ( isset($this->bymonthday) ) {
|
||||
$this->limit_byday(); /** Per RFC5545 3.3.10 from note 1 to table */
|
||||
if ( !isset($this->current_set[0]) ) return;
|
||||
if ( $this->freq == 'MONTHLY' || $this->freq == 'YEARLY' ) {
|
||||
if ( isset($this->bymonthday) || isset($this->byyearday) ) {
|
||||
$this->limit_byday(); /** Per RFC5545 3.3.10 from note 1&2 to table */
|
||||
return;
|
||||
}
|
||||
$first_of_month = $this->date_mask( clone($this->current_set[0]), null, null, 1, null, null, null);
|
||||
$dow_of_first = $first_of_month->format('w'); // 0 == Sunday
|
||||
$days_in_month = cal_days_in_month(CAL_GREGORIAN, $first_of_month->format('m'), $first_of_month->format('Y'));
|
||||
}
|
||||
$instances = $this->current_set;
|
||||
$this->current_set = array();
|
||||
foreach( $instances AS $k => $instance ) {
|
||||
if ( $this->freq == 'MONTHLY' ) {
|
||||
foreach( $this->byday AS $k => $weekday ) {
|
||||
if ( preg_match('{([+-])?(\d)?(MO|TU|WE|TH|FR|SA|SU)}', $weekday, $matches ) ) {
|
||||
$dow = (strpos('**SUMOTUWETHFRSA', $matches[3]) / 2) - 1;
|
||||
$first_dom = 1 + $dow - $dow_of_first; if ( $first_dom < 1 ) $first_dom +=7; // e.g. 1st=WE, dow=MO => 1+1-3=-1 => MO is 6th, etc.
|
||||
$whichweek = intval($matches[2]);
|
||||
if ( $GLOBALS['debug_rrule'] ) printf( "Expanding MONTHLY $weekday from date %s\n", $instance->format('c') );
|
||||
if ( $whichweek > 0 ) {
|
||||
$whichweek--;
|
||||
$monthday = $first_dom;
|
||||
if ( $matches[1] == '-' ) {
|
||||
$monthday += 35;
|
||||
while( $monthday > $days_in_month ) $monthday -= 7;
|
||||
$monthday -= (7 * $whichweek);
|
||||
}
|
||||
else {
|
||||
$monthday += (7 * $whichweek);
|
||||
}
|
||||
if ( $monthday > 0 && $monthday <= $days_in_month ) {
|
||||
$expanded = $this->date_mask( clone($instance), null, null, $monthday, null, null, null);
|
||||
if ( $GLOBALS['debug_rrule'] ) printf( "Expanded MONTHLY $weekday now $monthday into date %s\n", $expanded->format('c') );
|
||||
$this->current_set[] = $expanded;
|
||||
}
|
||||
}
|
||||
else {
|
||||
for( $monthday = $first_dom; $monthday <= $days_in_month; $monthday += 7 ) {
|
||||
$expanded = $this->date_mask( clone($instance), null, null, $monthday, null, null, null);
|
||||
if ( $GLOBALS['debug_rrule'] ) printf( "Expanded MONTHLY $weekday now $monthday into date %s\n", $expanded->format('c') );
|
||||
$this->current_set[] = $expanded;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
$this->expand_byday_in_month($instance);
|
||||
}
|
||||
else if ( $this->freq == 'WEEKLY' ) {
|
||||
/**
|
||||
* @TODO: This should really allow for WKST, since if we start a series
|
||||
* on (eg.) TH and interval > 1, a MO, TU, FR repeat will not be in the
|
||||
* same week with this code.
|
||||
*/
|
||||
$dow_of_instance = $instance->format('w'); // 0 == Sunday
|
||||
foreach( $this->byday AS $k => $weekday ) {
|
||||
$dow = (strpos('**SUMOTUWETHFRSA', $weekday) / 2) - 1;
|
||||
$offset = $dow - $dow_of_instance;
|
||||
if ( $offset < 0 ) $offset += 7;
|
||||
$this_expand = clone($instance);
|
||||
$this_expand->modify( sprintf('+%d day', $offset) );
|
||||
$this->current_set[] = $this_expand;
|
||||
if ( $GLOBALS['debug_rrule'] ) printf( "Expanded WEEKLY $weekday into date %s\n", $this_expand->format('c') );
|
||||
}
|
||||
$this->expand_byday_in_week($instance);
|
||||
}
|
||||
else { // YEARLY
|
||||
if ( isset($this->bymonth) ) {
|
||||
$this->expand_byday_in_month($instance);
|
||||
}
|
||||
else if ( isset($this->byweekno) ) {
|
||||
$this->expand_byday_in_week($instance);
|
||||
}
|
||||
else {
|
||||
$this->expand_byday_in_year($instance);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -327,18 +402,20 @@ class RepeatRule {
|
||||
$this->current_set = array();
|
||||
foreach( $instances AS $k => $instance ) {
|
||||
foreach( $this->{$element_name} AS $k => $element_value ) {
|
||||
/* if ( $GLOBALS['debug_rrule'] ) */ printf( "Limiting '$fmt_char' on '%s' => '%s' ?=? '%s' \n", $instance->format('c'), $instance->format($fmt_char), $element_value );
|
||||
if ( $GLOBALS['debug_rrule'] ) printf( "Limiting '$fmt_char' on '%s' => '%s' ?=? '%s' \n", $instance->format('c'), $instance->format($fmt_char), $element_value );
|
||||
if ( $instance->format($fmt_char) == $element_value ) $this->current_set[] = $instance;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function limit_byday() {
|
||||
global $rrule_day_numbers;
|
||||
|
||||
$fmt_char = 'w';
|
||||
$instances = $this->current_set;
|
||||
$this->current_set = array();
|
||||
foreach( $this->byday AS $k => $weekday ) {
|
||||
$dow = (strpos('**SUMOTUWETHFRSA', $weekday) / 2) - 1;
|
||||
$dow = $rrule_day_numbers[$weekday];
|
||||
foreach( $instances AS $k => $instance ) {
|
||||
if ( $GLOBALS['debug_rrule'] ) printf( "Limiting '$fmt_char' on '%s' => '%s' ?=? '%s' (%d) \n", $instance->format('c'), $instance->format($fmt_char), $weekday, $dow );
|
||||
if ( $instance->format($fmt_char) == $dow ) $this->current_set[] = $instance;
|
||||
|
||||
@ -24,6 +24,8 @@ class RRuleTest {
|
||||
var $recur;
|
||||
var $description;
|
||||
var $result_description;
|
||||
var $PHP_time;
|
||||
var $SQL_time;
|
||||
|
||||
function __construct( $description, $start, $recur, $result_description = null ) {
|
||||
$this->description = $description;
|
||||
@ -35,6 +37,7 @@ class RRuleTest {
|
||||
|
||||
function PHPTest() {
|
||||
$result = '';
|
||||
$start = microtime(true);
|
||||
$rule = new RepeatRule( $this->dtstart, $this->recur );
|
||||
$i = 0;
|
||||
while( $date = $rule->next() ) {
|
||||
@ -42,12 +45,14 @@ class RRuleTest {
|
||||
$result .= " " . $date->format('Y-m-d H:i:s');
|
||||
if ( $i >= $this->result_limit ) break;
|
||||
}
|
||||
$this->PHP_time = microtime(true) - $start;
|
||||
return $result;
|
||||
}
|
||||
|
||||
function SQLTest() {
|
||||
$result = '';
|
||||
$sql = "SELECT event_instances::timestamp AS event_date FROM event_instances(?,?) LIMIT ".$this->result_limit;
|
||||
$start = microtime(true);
|
||||
$qry = new AwlQuery($sql, $this->dtstart, $this->recur);
|
||||
// printf( "%s\n", $qry->querystring);
|
||||
if ( $qry->Exec("test") && $qry->rows() > 0 ) {
|
||||
@ -57,6 +62,7 @@ class RRuleTest {
|
||||
$result .= " " . $row->event_date;
|
||||
}
|
||||
}
|
||||
$this->SQL_time = microtime(true) - $start;
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
@ -71,17 +77,17 @@ $tests = array(
|
||||
, new RRuleTest( "Monthly forever", "20061104T073000", "RRULE:FREQ=MONTHLY" )
|
||||
, new RRuleTest( "Monthly, on the 1st monday, 2nd wednesday, 3rd friday and last sunday, forever", "20061117T073000", "RRULE:FREQ=MONTHLY;BYDAY=1MO,2WE,3FR,-1SU" )
|
||||
, new RRuleTest( "The working days of each month", "20061107T113000", "RRULE:FREQ=MONTHLY;BYDAY=MO,TU,WE,TH,FR;UNTIL=20070101T000000" )
|
||||
, new RRuleTest( "The last working day of each month", "20061107T113000", "RRULE:FREQ=MONTHLY;BYDAY=MO,TU,WE,TH,FR;BYSETPOS=-1" )
|
||||
, new RRuleTest( "Every working day", "20081020T103000", "RRULE:FREQ=MONTHLY;BYDAY=MO,TU,WE,TH,FR" )
|
||||
, new RRuleTest( "Every working day", "20081020T110000", "RRULE:FREQ=DAILY;INTERVAL=1;BYDAY=MO,TU,WE,TH,FR" )
|
||||
// , 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( "Every tuesday and friday", "20081017T084500", "RRULE:FREQ=MONTHLY;INTERVAL=1;BYDAY=TU,FR" )
|
||||
// , new RRuleTest( "Every tuesday and friday", "20081017T084500", "RRULE:FREQ=WEEKLY;INTERVAL=1;BYDAY=TU,FR" )
|
||||
// , new RRuleTest( "Every tuesday and friday", "20081017T084500", "RRULE:FREQ=DAILY;INTERVAL=1;BYDAY=TU,FR" )
|
||||
// , new RRuleTest( "Time zone 1", "19700315T030000", "FREQ=YEARLY;INTERVAL=1;BYDAY=3SU;BYMONTH=3" )
|
||||
// , new RRuleTest( "Time zone 2", "19700927T020000", "FREQ=YEARLY;INTERVAL=1;BYDAY=-1SU;BYMONTH=9" )
|
||||
// , new RRuleTest( "Time zone 3", "19810329T030000", "FREQ=YEARLY;BYMONTH=3;BYDAY=-1SU" )
|
||||
// , new RRuleTest( "Time zone 4", "20000404T020000", "FREQ=YEARLY;BYDAY=1SU;BYMONTH=4" )
|
||||
, 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( "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" )
|
||||
, new RRuleTest( "Time zone 1", "19700315T030000", "FREQ=YEARLY;INTERVAL=1;BYDAY=3SU;BYMONTH=3" )
|
||||
, new RRuleTest( "Time zone 2", "19700927T020000", "FREQ=YEARLY;INTERVAL=1;BYDAY=-1SU;BYMONTH=9" )
|
||||
, new RRuleTest( "Time zone 3", "19810329T030000", "FREQ=YEARLY;BYMONTH=3;BYDAY=-1SU" )
|
||||
, new RRuleTest( "Time zone 4", "20000404T010000", "FREQ=YEARLY;BYDAY=1SU;BYMONTH=4;COUNT=15" )
|
||||
);
|
||||
|
||||
foreach( $tests AS $k => $test ) {
|
||||
@ -91,11 +97,10 @@ foreach( $tests AS $k => $test ) {
|
||||
$php_result = $test->PHPTest();
|
||||
$sql_result = $test->SQLTest();
|
||||
if ( $php_result == $sql_result ) {
|
||||
echo "PHP & SQL results are identical (-:";
|
||||
echo "$php_result\n";
|
||||
printf( 'PHP & SQL results are identical (-: P: %6.4lf & S: %6.4lf'."\n", $test->PHP_time, $test->SQL_time);
|
||||
}
|
||||
else {
|
||||
echo "PHP & SQL results differ :-(\n";
|
||||
printf( 'PHP & SQL results differ :-( P: %6.4lf & S: %6.4lf'."\n", $test->PHP_time, $test->SQL_time);
|
||||
echo "PHP Result:\n$php_result\n\n";
|
||||
echo "SQL Result:\n$sql_result\n\n"; // Still under development
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user