45 if (!$this->
valid())
return;
70 if (!is_null($this->count)) {
73 return is_null($this->until) || $this->currentDate <=
$this->until;
98 switch ($this->frequency) {
148 while ($this->
valid() && $this->currentDate < $dt) {
325 $this->currentDate = $this->currentDate->modify(
'+' . $this->interval .
' hours');
336 if (!$this->byHour && !$this->byDay) {
337 $this->currentDate = $this->currentDate->modify(
'+' . $this->interval .
' days');
341 if (!empty($this->byHour)) {
342 $recurrenceHours = $this->
getHours();
345 if (!empty($this->byDay)) {
346 $recurrenceDays = $this->
getDays();
349 if (!empty($this->byMonth)) {
355 if ($this->currentDate->format(
'G') ==
'23') {
357 $this->currentDate = $this->currentDate->modify(
'+' . $this->interval - 1 .
' days');
360 $this->currentDate = $this->currentDate->modify(
'+1 hours');
363 $this->currentDate = $this->currentDate->modify(
'+' . $this->interval .
' days');
368 $currentMonth = $this->currentDate->format(
'n');
371 $currentDay = $this->currentDate->format(
'w');
374 $currentHour = $this->currentDate->format(
'G');
377 ($this->byDay && !in_array($currentDay, $recurrenceDays)) ||
378 ($this->byHour && !in_array($currentHour, $recurrenceHours)) ||
379 ($this->byMonth && !in_array($currentMonth, $recurrenceMonths))
391 if (!$this->byHour && !$this->byDay) {
392 $this->currentDate = $this->currentDate->modify(
'+' . $this->interval .
' weeks');
397 $recurrenceHours = $this->
getHours();
401 $recurrenceDays = $this->
getDays();
410 $this->currentDate = $this->currentDate->modify(
'+1 hours');
412 $this->currentDate = $this->currentDate->modify(
'+1 days');
416 $currentDay = (int)$this->currentDate->format(
'w');
419 $currentHour = (int)$this->currentDate->format(
'G');
422 if ($currentDay === $firstDay && (!$this->byHour || $currentHour ==
'0')) {
423 $this->currentDate = $this->currentDate->modify(
'+' . $this->interval - 1 .
' weeks');
427 if ($this->currentDate->format(
'w') != $firstDay) {
428 $this->currentDate = $this->currentDate->modify(
'last ' . $this->dayNames[$this->dayMap[$this->weekStart]]);
433 }
while (($this->byDay && !in_array($currentDay, $recurrenceDays)) || ($this->byHour && !in_array($currentHour, $recurrenceHours)));
443 $currentDayOfMonth = $this->currentDate->format(
'j');
444 if (!$this->byMonthDay && !$this->byDay) {
449 if ($currentDayOfMonth < 29) {
450 $this->currentDate = $this->currentDate->modify(
'+' . $this->interval .
' months');
456 $tempDate = $tempDate->modify(
'+ ' . ($this->interval * $increase) .
' months');
457 }
while ($tempDate->format(
'j') != $currentDayOfMonth);
458 $this->currentDate = $tempDate;
467 foreach ($occurrences as $occurrence) {
471 if ($occurrence > $currentDayOfMonth) {
484 $this->currentDate =
new DateTimeImmutable($this->currentDate->format(
'Y-m-1 H:i:s'), $this->currentDate->getTimezone());
486 $this->currentDate = $this->currentDate->modify(
'+ ' . $this->interval .
' months');
490 $currentDayOfMonth = 0;
494 $this->currentDate = $this->currentDate->setDate(
495 (
int)$this->currentDate->format(
'Y'),
496 (int)$this->currentDate->format(
'n'),
509 $currentMonth = $this->currentDate->format(
'n');
510 $currentYear = $this->currentDate->format(
'Y');
511 $currentDayOfMonth = $this->currentDate->format(
'j');
514 if (empty($this->byMonth)) {
517 if ($currentMonth == 2 && $currentDayOfMonth == 29) {
531 $nextDate = $nextDate->modify(
'+ ' . ($this->interval *
$counter) .
' years');
532 }
while ($nextDate->format(
'n') != 2);
534 $this->currentDate = $nextDate;
540 if ($this->byWeekNo !== null) {
543 foreach ($this->byDay as
$byDay) {
544 $dayOffsets[] = $this->dayMap[
$byDay];
550 $currentYear = $this->currentDate->format(
'Y');
557 foreach ($dayOffsets as $dayOffset) {
559 $date->setISODate($currentYear, $byWeekNo, $dayOffset);
561 if ($date > $this->currentDate) {
562 $checkDates[] = $date;
567 if (count($checkDates) > 0) {
568 $this->currentDate = min($checkDates);
577 if ($this->byYearDay !== null) {
580 foreach ($this->byDay as
$byDay) {
581 $dayOffsets[] = $this->dayMap[
$byDay];
584 $dayOffsets = [1,2,3,4,5,6,7];
587 $currentYear = $this->currentDate->format(
'Y');
595 $date = $date->setDate($currentYear, 1, 1);
596 if ($byYearDay > 0) {
597 $date = $date->add(
new \
DateInterval(
'P' . $byYearDay .
'D'));
599 $date = $date->sub(
new \
DateInterval(
'P' . abs($byYearDay) .
'D'));
602 if ($date > $this->currentDate && in_array($date->format(
'N'), $dayOffsets)) {
603 $checkDates[] = $date;
607 if (count($checkDates) > 0) {
608 $this->currentDate = min($checkDates);
618 $this->currentDate = $this->currentDate->modify(
'+' . $this->interval .
' years');
623 $currentMonth = $this->currentDate->format(
'n');
624 $currentYear = $this->currentDate->format(
'Y');
625 $currentDayOfMonth = $this->currentDate->format(
'j');
627 $advancedToNewMonth =
false;
631 if ($this->byDay || $this->byMonthDay) {
637 foreach ($occurrences as $occurrence) {
643 if ($occurrence > $currentDayOfMonth || $advancedToNewMonth) {
651 $currentDayOfMonth = 1;
652 $advancedToNewMonth =
true;
656 if ($currentMonth > 12) {
660 }
while (!in_array($currentMonth, $this->byMonth));
662 $this->currentDate = $this->currentDate->setDate(
665 (
int)$currentDayOfMonth
671 $this->currentDate = $this->currentDate->setDate(
685 if ($currentMonth > 12) {
689 }
while (!in_array($currentMonth, $this->byMonth));
690 $this->currentDate = $this->currentDate->setDate(
693 (
int)$currentDayOfMonth
714 if (is_string($rrule)) {
718 foreach ($rrule as
$key => $value) {
724 $value = strtolower($value);
727 [
'secondly',
'minutely',
'hourly',
'daily',
'weekly',
'monthly',
'yearly']
731 $this->frequency = $value;
745 if ($this->until < $this->startDate) {
763 $this->bySecond = (array)$value;
767 $this->byMinute = (array)$value;
771 $this->byHour = (array)$value;
775 $value = (array)$value;
776 foreach ($value as $part) {
777 if (!preg_match(
'#^ (-|\+)? ([1-5])? (MO|TU|WE|TH|FR|SA|SU) $# xi', $part)) {
781 $this->byDay = $value;
785 $this->byMonthDay = (array)$value;
789 $this->byYearDay = (array)$value;
791 if (!is_numeric($byYearDay) || (int)$byYearDay < -366 || (
int)$byYearDay == 0 || (int)$byYearDay > 366) {
792 throw new InvalidDataException(
'BYYEARDAY in RRULE must have value(s) from 1 to 366, or -366 to -1!');
798 $this->byWeekNo = (array)$value;
800 if (!is_numeric($byWeekNo) || (int)$byWeekNo < -53 || (
int)$byWeekNo == 0 || (int)$byWeekNo > 53) {
801 throw new InvalidDataException(
'BYWEEKNO in RRULE must have value(s) from 1 to 53, or -53 to -1!');
807 $this->byMonth = (array)$value;
808 foreach ($this->byMonth as
$byMonth) {
809 if (!is_numeric($byMonth) || (int)$byMonth < 1 || (
int)$byMonth > 12) {
816 $this->bySetPos = (array)$value;
820 $this->weekStart = strtoupper($value);
863 if ($this->byDay)
foreach ($this->byDay as $day) {
865 $dayName = $this->dayNames[$this->dayMap[substr($day, -2)]];
873 $checkDate = new \DateTime(
$startDate->format(
'Y-m-1'));
875 if ($checkDate->format(
'l') !== $dayName) {
876 $checkDate = $checkDate->modify($dayName);
880 $dayHits[] = $checkDate->format(
'j');
881 $checkDate = $checkDate->modify(
'next ' . $dayName);
882 }
while ($checkDate->format(
'n') ===
$startDate->format(
'n'));
887 if (strlen($day) > 2) {
888 $offset = (int)substr($day, 0, -2);
893 if (isset($dayHits[$offset - 1])) {
894 $byDayResults[] = $dayHits[$offset - 1];
900 if (isset($dayHits[count($dayHits) + $offset])) {
901 $byDayResults[] = $dayHits[count($dayHits) + $offset];
907 $byDayResults = array_merge($byDayResults, $dayHits);
913 $byMonthDayResults = [];
914 if ($this->byMonthDay)
foreach ($this->byMonthDay as $monthDay) {
922 $byMonthDayResults[] = $monthDay;
925 $byMonthDayResults[] =
$startDate->format(
't') + 1 + $monthDay;
932 if ($this->byMonthDay && $this->byDay) {
933 $result = array_intersect($byMonthDayResults, $byDayResults);
934 } elseif ($this->byMonthDay) {
944 if (!$this->bySetPos) {
948 $filteredResult = [];
949 foreach ($this->bySetPos as $setPos) {
952 $setPos = count(
$result) + ($setPos + 1);
954 if (isset(
$result[$setPos - 1])) {
955 $filteredResult[] =
$result[$setPos - 1];
959 sort($filteredResult, SORT_NUMERIC);
960 return $filteredResult;
981 $recurrenceHours = [];
982 foreach ($this->byHour as
$byHour) {
986 return $recurrenceHours;
991 $recurrenceDays = [];
992 foreach ($this->byDay as
$byDay) {
997 $recurrenceDays[] = $this->dayMap[substr($byDay, -2)];
1001 return $recurrenceDays;
1006 $recurrenceMonths = [];
1007 foreach ($this->byMonth as
$byMonth) {
1011 return $recurrenceMonths;
getMonthlyOccurrences()
Returns all the occurrences for a monthly frequency with a 'byDay' or 'byMonthDay' expansion for the ...
isInfinite()
Returns true if this recurring event never ends.
static parse($date, $referenceTz=null)
Parses either a Date or DateTime, or Duration value.
rewind()
Resets the iterator.
nextYearly()
Does the processing for advancing the iterator for yearly frequency.
nextDaily()
Does the processing for advancing the iterator for daily frequency.
nextMonthly()
Does the processing for advancing the iterator for monthly frequency.
nextHourly()
Does the processing for advancing the iterator for hourly frequency.
key()
Returns the current item number.
next()
Goes on to the next iteration.
parseRRule($rrule)
This method receives a string from an RRULE property, and populates this class with all the values...
__construct($rrule, DateTimeInterface $start)
Creates the Iterator.
static stringToArray($value)
Parses an RRULE value string, and turns it into a struct-ish array.
fastForward(DateTimeInterface $dt)
This method allows you to quickly go to the next occurrence after the specified date.
valid()
Returns whether the current item is a valid item for the recurrence iterator.
nextWeekly()
Does the processing for advancing the iterator for weekly frequency.
This exception is thrown whenever an invalid value is found anywhere in a iCalendar or vCard object...