Skip to content

Commit

Permalink
Add daily occurences to nextMonth and NextYear
Browse files Browse the repository at this point in the history
  • Loading branch information
KAYLukas committed Nov 29, 2018
1 parent 5b86533 commit 8ba0746
Show file tree
Hide file tree
Showing 2 changed files with 145 additions and 22 deletions.
127 changes: 105 additions & 22 deletions lib/Recur/RRuleIterator.php
Original file line number Diff line number Diff line change
Expand Up @@ -417,6 +417,9 @@ protected function nextWeekly()
protected function nextMonthly()
{
$currentDayOfMonth = $this->currentDate->format('j');
$currentHourOfMonth = $this->currentDate->format('G');
$currentMinuteOfMonth = $this->currentDate->format('i');
$currentSecondOfMonth = $this->currentDate->format('s');
if (!$this->byMonthDay && !$this->byDay) {
// If the current day is higher than the 28th, rollover can
// occur to the next month. We Must skip these invalid
Expand All @@ -442,7 +445,23 @@ protected function nextMonthly()
foreach ($occurrences as $occurrence) {
// The first occurrence thats higher than the current
// day of the month wins.
if ($occurrence > $currentDayOfMonth) {
if ($occurrence[0] > $currentDayOfMonth) {
break 2;
} elseif ($occurrence[0] < $currentDayOfMonth) {
continue;
}
if ($occurrence[1] > $currentHourOfMonth) {
break 2;
} elseif ($occurrence[1] < $currentHourOfMonth) {
continue;
}

if ($occurrence[2] > $currentMinuteOfMonth) {
break 2;
} elseif ($occurrence[2] < $currentMinuteOfMonth) {
continue;
}
if ($occurrence[3] > $currentSecondOfMonth) {
break 2;
}
}
Expand All @@ -461,13 +480,16 @@ protected function nextMonthly()
// This goes to 0 because we need to start counting at the
// beginning.
$currentDayOfMonth = 0;
$currentHourOfMonth = 0;
$currentMinuteOfMonth = 0;
$currentSecondOfMonth = 0;
}

$this->currentDate = $this->currentDate->setDate(
(int) $this->currentDate->format('Y'),
(int) $this->currentDate->format('n'),
(int) $occurrence
);
$occurrence[0]
)->setTime($occurrence[1], $occurrence[2], $occurrence[3]);
}

/**
Expand All @@ -478,6 +500,9 @@ protected function nextYearly()
$currentMonth = $this->currentDate->format('n');
$currentYear = $this->currentDate->format('Y');
$currentDayOfMonth = $this->currentDate->format('j');
$currentHourOfMonth = $this->currentDate->format('G');
$currentMinuteOfMonth = $this->currentDate->format('i');
$currentSecondOfMonth = $this->currentDate->format('s');

// No sub-rules, so we just advance by year
if (empty($this->byMonth)) {
Expand Down Expand Up @@ -588,25 +613,38 @@ protected function nextYearly()
return;
}

$currentMonth = $this->currentDate->format('n');
$currentYear = $this->currentDate->format('Y');
$currentDayOfMonth = $this->currentDate->format('j');

$advancedToNewMonth = false;

// If we got a byDay or getMonthDay filter, we must first expand
// further.
if ($this->byDay || $this->byMonthDay) {
while (true) {
$occurrences = $this->getMonthlyOccurrences();

foreach ($occurrences as $occurrence) {
// The first occurrence that's higher than the current
// day of the month wins.
// If we advanced to the next month or year, the first
// occurrence is always correct.
if ($occurrence > $currentDayOfMonth || $advancedToNewMonth) {
break 2;
// If the start date is incorrect we must directly jump to the next value
if (in_array($currentMonth, $this->byMonth)) {
$occurrences = $this->getMonthlyOccurrences();
foreach ($occurrences as $occurrence) {
// The first occurrence that's higher than the current
// day of the month wins.
// If we advanced to the next month or year, the first
// occurrence is always correct.
if ($occurrence[0] > $currentDayOfMonth || $advancedToNewMonth) {
break 2;
} elseif ($occurrence[0] < $currentDayOfMonth) {
continue;
}
if ($occurrence[1] > $currentHourOfMonth) {
break 2;
} elseif ($occurrence[1] < $currentHourOfMonth) {
continue;
}
if ($occurrence[2] > $currentMinuteOfMonth) {
break 2;
} elseif ($occurrence[2] < $currentMinuteOfMonth) {
continue;
}
if ($occurrence[3] > $currentSecondOfMonth) {
break 2;
}
}
}

Expand All @@ -633,8 +671,8 @@ protected function nextYearly()
$this->currentDate = $this->currentDate->setDate(
(int) $currentYear,
(int) $currentMonth,
(int) $occurrence
);
(int) $occurrence[0]
)->setTime($occurrence[1], $occurrence[2], $occurrence[3]);

return;
} else {
Expand Down Expand Up @@ -798,7 +836,8 @@ protected function parseRRule($rrule)
* Returns all the occurrences for a monthly frequency with a 'byDay' or
* 'byMonthDay' expansion for the current month.
*
* The returned list is an array of integers with the day of month (1-31).
* The returned list is an array of arrays with as first element the day of month (1-31);
* the hour; the minute and second of the occurence
*
* @return array
*/
Expand Down Expand Up @@ -884,8 +923,23 @@ protected function getMonthlyOccurrences()
} else {
$result = $byDayResults;
}
$result = array_unique($result);
sort($result, SORT_NUMERIC);

$result = $this->addDailyOccurences($result);
$result = array_unique($result, SORT_REGULAR);
$sortLex = function ($a, $b) {
if ($a[0] != $b[0]) {
return $a[0] - $b[0];
}
if ($a[1] != $b[1]) {
return $a[1] - $b[1];
}
if ($a[2] != $b[2]) {
return $a[2] - $b[2];
}

return $a[3] - $b[3];
};
usort($result, $sortLex);

// The last thing that needs checking is the BYSETPOS. If it's set, it
// means only certain items in the set survive the filter.
Expand All @@ -903,11 +957,40 @@ protected function getMonthlyOccurrences()
}
}

sort($filteredResult, SORT_NUMERIC);
usort($result, $sortLex);

return $filteredResult;
}

/**
* Expends daily occurrences to an array of days that an event occurs on.
*
* @param array $result an array of integers with the day of month (1-31);
*
* @return array an array of arrays with the day of the month, hours, minute and seconds of the occurence
*/
protected function addDailyOccurences(array $result)
{
$output = [];
$hour = (int) $this->currentDate->format('G');
$minute = (int) $this->currentDate->format('i');
$second = (int) $this->currentDate->format('s');
foreach ($result as $day) {
$seconds = $this->bySecond ? $this->bySecond : [$second];
$minutes = $this->byMinute ? $this->byMinute : [$minute];
$hours = $this->byHour ? $this->byHour : [$hour];
foreach ($hours as $h) {
foreach ($minutes as $m) {
foreach ($seconds as $s) {
$output[] = [(int) $day, (int) $h, (int) $m, (int) $s];
}
}
}
}

return $output;
}

/**
* Simple mapping from iCalendar day names to day numbers.
*
Expand Down
40 changes: 40 additions & 0 deletions tests/VObject/Recur/RRuleIteratorTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -576,6 +576,46 @@ public function testYearlyByYearDayNegative()
);
}

public function testFirstLastSundayEveryOtherYearAt1530and1730InJanuary()
{
$this->parse('FREQ=YEARLY;INTERVAL=2;BYMONTH=1;BYDAY=1SU,-1SU;BYHOUR=15,17;BYMINUTE=30,35;BYSECOND=15,56',
'1999-12-01 12:34:56',
[
'1999-12-01 12:34:56',
'2001-01-07 15:30:15', '2001-01-07 15:30:56', '2001-01-07 15:35:15', '2001-01-07 15:35:56',
'2001-01-07 17:30:15', '2001-01-07 17:30:56', '2001-01-07 17:35:15', '2001-01-07 17:35:56',

'2001-01-28 15:30:15', '2001-01-28 15:30:56', '2001-01-28 15:35:15', '2001-01-28 15:35:56',
'2001-01-28 17:30:15', '2001-01-28 17:30:56', '2001-01-28 17:35:15', '2001-01-28 17:35:56',

'2003-01-05 15:30:15', '2003-01-05 15:30:56', '2003-01-05 15:35:15', '2003-01-05 15:35:56',
'2003-01-05 17:30:15', '2003-01-05 17:30:56', '2003-01-05 17:35:15', '2003-01-05 17:35:56',

'2003-01-26 15:30:15', '2003-01-26 15:30:56', '2003-01-26 15:35:15', '2003-01-26 15:35:56',
'2003-01-26 17:30:15', '2003-01-26 17:30:56', '2003-01-26 17:35:15', '2003-01-26 17:35:56',
]);
}

public function testFirstFourthSundayEveryOtherMonthAt830and930()
{
$this->parse('FREQ=MONTHLY;INTERVAL=2;BYDAY=1SU,4SU;BYHOUR=15,17;BYMINUTE=30,32;BYSECOND=11,12',
'2001-01-01 12:34:56',
[
'2001-01-01 12:34:56',
'2001-01-07 15:30:11', '2001-01-07 15:30:12', '2001-01-07 15:32:11', '2001-01-07 15:32:12',
'2001-01-07 17:30:11', '2001-01-07 17:30:12', '2001-01-07 17:32:11', '2001-01-07 17:32:12',

'2001-01-28 15:30:11', '2001-01-28 15:30:12', '2001-01-28 15:32:11', '2001-01-28 15:32:12',
'2001-01-28 17:30:11', '2001-01-28 17:30:12', '2001-01-28 17:32:11', '2001-01-28 17:32:12',

'2001-03-04 15:30:11', '2001-03-04 15:30:12', '2001-03-04 15:32:11', '2001-03-04 15:32:12',
'2001-03-04 17:30:11', '2001-03-04 17:30:12', '2001-03-04 17:32:11', '2001-03-04 17:32:12',

'2001-03-25 15:30:11', '2001-03-25 15:30:12', '2001-03-25 15:32:11', '2001-03-25 15:32:12',
'2001-03-25 17:30:11', '2001-03-25 17:30:12', '2001-03-25 17:32:11', '2001-03-25 17:32:12',
]);
}

/**
* @expectedException \Sabre\VObject\InvalidDataException
*/
Expand Down

0 comments on commit 8ba0746

Please sign in to comment.