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);
 
  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;
 
  792                            throw new InvalidDataException(
'BYYEARDAY in RRULE must have value(s) from 1 to 366, or -366 to -1!');
 
  798                    $this->byWeekNo = (array)$value;
 
  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) {
 
  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) {
 
  918                $monthDay < 0 - $startDate->format(
't')) {
 
  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;
 
An exception for terminatinating execution or to throw for unit testing.
static parse($date, $referenceTz=null)
Parses either a Date or DateTime, or Duration value.
This exception is thrown whenever an invalid value is found anywhere in a iCalendar or vCard object.
static stringToArray($value)
Parses an RRULE value string, and turns it into a struct-ish array.
nextDaily()
Does the processing for advancing the iterator for daily frequency.
key()
Returns the current item number.
nextMonthly()
Does the processing for advancing the iterator for monthly frequency.
nextHourly()
Does the processing for advancing the iterator for hourly frequency.
next()
Goes on to the next iteration.
isInfinite()
Returns true if this recurring event never ends.
getMonthlyOccurrences()
Returns all the occurrences for a monthly frequency with a 'byDay' or 'byMonthDay' expansion for the ...
fastForward(DateTimeInterface $dt)
This method allows you to quickly go to the next occurrence after the specified date.
__construct($rrule, DateTimeInterface $start)
Creates the Iterator.
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.
nextYearly()
Does the processing for advancing the iterator for yearly frequency.
parseRRule($rrule)
This method receives a string from an RRULE property, and populates this class with all the values.
rewind()
Resets the iterator.