mirror of
https://gitlab.com/davical-project/davical.git
synced 2026-03-13 08:00:15 +00:00
An RRule library that seems to cover most of the basic and some of the not
so basic use of RRULE in iCalendar. And a few test cases.
This commit is contained in:
parent
5a72ac12b5
commit
530409877f
106
inc/RRule.php
106
inc/RRule.php
@ -408,9 +408,8 @@ class iCalDate {
|
||||
function GetMonthByMonthDay($bymonthday) {
|
||||
dbg_error_log( "RRule", " Applying BYMONTHDAY %s to month", $bymonthday );
|
||||
$days_in_month = $this->DaysInMonth();
|
||||
$dayrules = split(',',$byday);
|
||||
$dayrules = split(',',$bymonthday);
|
||||
$set = array();
|
||||
$first_dow = (date('w',$this->_epoch) - $this->_dd + 36) % 7;
|
||||
foreach( $dayrules AS $k => $v ) {
|
||||
$v = intval($v);
|
||||
if ( $v > 0 && $v <= $days_in_month ) $set[$v] = 1;
|
||||
@ -418,12 +417,36 @@ class iCalDate {
|
||||
return $set;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Applies any BYDAY to the week to return a set of days
|
||||
* @param string $byday The BYDAY rule
|
||||
* @return array An array of the day numbers for the week which meet the rule.
|
||||
*/
|
||||
function GetWeekByDay($byday) {
|
||||
global $ical_weekdays;
|
||||
dbg_error_log( "RRule", " Applying BYDAY %s to week", $byday );
|
||||
$days = split(',',$byday);
|
||||
$dow = date('w',$this->_epoch);
|
||||
foreach( $days AS $k => $v ) {
|
||||
$daynum = $ical_weekdays[$v];
|
||||
$dd = $this->_dd - $dow + $daynum;
|
||||
if ( $daynum < $this->_wkst ) $dd += 7;
|
||||
$set[$dd] = 1;
|
||||
}
|
||||
return $set;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Test if $this is greater than the date parameter
|
||||
* @param string $lesser The other date, as a local time string
|
||||
* @return boolean True if $this > $lesser
|
||||
*/
|
||||
function GreaterThan($lesser) {
|
||||
if ( is_object($lesser) ) {
|
||||
return ( $this->_text > $lesser->_text );
|
||||
}
|
||||
return ( $this->_text > $lesser ); // These sorts of dates are designed that way...
|
||||
}
|
||||
|
||||
@ -471,6 +494,32 @@ class iCalDate {
|
||||
return $set;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Given set position descriptions like '1', '3', '11', '-3' or '-1' and a set,
|
||||
* return the subset matching the list of set positions.
|
||||
*
|
||||
* @param string $bysplist The list of set positions.
|
||||
* @param string $set The set of days that we will apply the positions to.
|
||||
*
|
||||
* @return array The subset which matches.
|
||||
*/
|
||||
function &ApplyBySetPos($bysplist, &$set) {
|
||||
dbg_error_log( "RRule", " Applying '%s' to set of %d days", $bysplist, count($set) );
|
||||
$subset = array();
|
||||
$max = count($set);
|
||||
$positions = split( '[^0-9-]', $bysplist );
|
||||
foreach( $positions AS $k => $v ) {
|
||||
if ( $v < 0 ) {
|
||||
$v += $max;
|
||||
}
|
||||
else {
|
||||
$v--;
|
||||
}
|
||||
$subset[] = $set[$v];
|
||||
}
|
||||
return $subset;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -710,6 +759,15 @@ class RRule {
|
||||
$days = $next->ApplyBySetpos($this->_part['BYSETPOS'], $days);
|
||||
}
|
||||
|
||||
if ( ! $next->GreaterThan($this->_first) ) {
|
||||
dbg_error_log( "RRule", " Removing dates less than or equal to %s", $this->_first->Render() );
|
||||
foreach( $days AS $k => $v ) {
|
||||
if ( $k <= $this->_first->_dd ) {
|
||||
unset($days[$k]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
while( $limit && count($days) < 1 );
|
||||
|
||||
@ -724,19 +782,59 @@ class RRule {
|
||||
else {
|
||||
$this->_started = true;
|
||||
}
|
||||
$days[$next->_dd] = 1;
|
||||
|
||||
if ( isset($this->_part['BYDAY']) ) {
|
||||
$days = $next->GetWeekByDay($this->_part['BYDAY']);
|
||||
}
|
||||
else
|
||||
$days[$next->_dd] = 1;
|
||||
|
||||
if ( isset($this->_part['BYSETPOS']) ) {
|
||||
$days = $next->ApplyBySetpos($this->_part['BYSETPOS'], $days);
|
||||
}
|
||||
|
||||
if ( ! $next->GreaterThan($this->_first) ) {
|
||||
dbg_error_log( "RRule", " Removing dates less than or equal to %s", $this->_first->Render() );
|
||||
foreach( $days AS $day => $v ) {
|
||||
if ( $day <= $this->_first->_dd ) {
|
||||
unset($days[$day]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
while( $limit && count($days) < 1 );
|
||||
}
|
||||
|
||||
$i = 0;
|
||||
$next_month = false;
|
||||
$prev_month = false;
|
||||
$days_in_month = $next->DaysInMonth();
|
||||
foreach( $days AS $day => $v ) {
|
||||
if ( $day > $days_in_month ) {
|
||||
$next_month = true;
|
||||
$next->SetMonthDay($days_in_month);
|
||||
$next->AddDays(1);
|
||||
}
|
||||
else if ( $day < 1 ) {
|
||||
$prev_month = true;
|
||||
$next->SetMonthDay(1);
|
||||
$next->AddDays(-1);
|
||||
$days_in_month = $next->DaysInMonth();
|
||||
}
|
||||
|
||||
if ( $next_month ) {
|
||||
$day -= $days_in_month;
|
||||
}
|
||||
else if ( $prev_month ) {
|
||||
$day += $days_in_month;
|
||||
}
|
||||
|
||||
$next->SetMonthDay($day);
|
||||
if ( isset($this->_part['UNTIL']) && $next->GreaterThan($this->_part['UNTIL']) ) {
|
||||
$this->_finished = true;
|
||||
continue;
|
||||
}
|
||||
/** FIXME should check this is greater than the first date here too */
|
||||
|
||||
$this->_dates[$this->_current + $i] = $next;
|
||||
$i++;
|
||||
}
|
||||
|
||||
@ -13,8 +13,10 @@ $tests = array(
|
||||
"20061103T073000" => "RRULE:FREQ=DAILY;COUNT=7"
|
||||
, "20061102T100000" => "RRULE:FREQ=WEEKLY;COUNT=26;INTERVAL=1;BYDAY=TH"
|
||||
, "20061103T160000" => "RRULE:FREQ=WEEKLY;INTERVAL=2;UNTIL=20071222T235900"
|
||||
, "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"
|
||||
, "20061107T113000" => "RRULE:FREQ=MONTHLY;BYDAY=MO,TU,WE,TH,FR;BYSETPOS=-1"
|
||||
);
|
||||
|
||||
foreach( $tests AS $start => $rrule ) {
|
||||
@ -29,7 +31,7 @@ foreach( $tests AS $start => $rrule ) {
|
||||
echo " " . $date->Render();
|
||||
}
|
||||
}
|
||||
while( isset($date) && $i++ < 50 );
|
||||
while( isset($date) && $i++ < 30 );
|
||||
|
||||
echo "\n\n\n";
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user