@@ -85,30 +85,27 @@ public function rewind()
8585
8686 /**
8787 * Goes on to the next iteration.
88+ * @param int $amount
8889 */
89- public function next ()
90+ public function next ($ amount = 1 )
9091 {
9192 // Otherwise, we find the next event in the normal RRULE
9293 // sequence.
9394 switch ($ this ->frequency ) {
94- case 'hourly ' :
95- $ this ->nextHourly ();
95+ case 'hourly ' :
96+ $ this ->nextHourly ($ amount );
9697 break ;
97-
98- case 'daily ' :
99- $ this ->nextDaily ();
98+ case 'daily ' :
99+ $ this ->nextDaily ($ amount );
100100 break ;
101-
102- case 'weekly ' :
103- $ this ->nextWeekly ();
101+ case 'weekly ' :
102+ $ this ->nextWeekly ($ amount );
104103 break ;
105-
106- case 'monthly ' :
107- $ this ->nextMonthly ();
104+ case 'monthly ' :
105+ $ this ->nextMonthly ($ amount );
108106 break ;
109-
110- case 'yearly ' :
111- $ this ->nextYearly ();
107+ case 'yearly ' :
108+ $ this ->nextYearly ($ amount );
112109 break ;
113110 }
114111 ++$ this ->counter ;
@@ -134,9 +131,49 @@ public function isInfinite()
134131 */
135132 public function fastForward (DateTimeInterface $ dt )
136133 {
134+ if (!isset ($ this ->count )) {
135+ do {
136+ $ diff = $ this ->currentDate ->diff ($ dt );
137+ switch ($ this ->frequency ) {
138+ case 'hourly ' :
139+ $ i = $ diff ->days * 24 ;
140+ break ;
141+ case 'daily ' :
142+ $ i = $ diff ->days ;
143+ break ;
144+ case 'weekly ' :
145+ $ i = $ diff ->days / 7 ;
146+ break ;
147+ case 'monthly ' :
148+ $ i = $ diff ->days / 30 ;
149+ break ;
150+ case 'yearly ' :
151+ $ i = $ diff ->days / 365 ;
152+ break ;
153+ }
154+ $ i /= $ this ->interval ;
155+ $ i /= 4 ;
156+ $ i = floor ($ i );
157+ $ i = max (1 , $ i );
158+ do {
159+ $ previousDate = clone $ this ->currentDate ;
160+ $ this ->next ($ i );
161+ } while ($ this ->valid () && $ this ->currentDate < $ dt );
162+
163+ $ this ->currentDate = $ previousDate ;
164+ // do one step to avoid deadlock
165+ $ this ->next ();
166+ } while ($ i > 5 && $ this ->valid () && $ this ->currentDate < $ dt );
167+ }
168+
137169 while ($ this ->valid () && $ this ->currentDate < $ dt ) {
138170 $ this ->next ();
139171 }
172+
173+ if (!isset ($ this ->count )) {
174+ // We don't know the counter at this point anymore
175+ $ this ->counter = NAN ;
176+ }
140177 }
141178
142179 /**
@@ -306,19 +343,18 @@ public function fastForward(DateTimeInterface $dt)
306343 /**
307344 * Does the processing for advancing the iterator for hourly frequency.
308345 */
309- protected function nextHourly ()
346+ protected function nextHourly ($ amount = 1 )
310347 {
311- $ this ->currentDate = $ this ->currentDate ->modify ('+ ' .$ this ->interval .' hours ' );
348+ $ this ->currentDate = $ this ->currentDate ->modify ('+ ' .$ amount * $ this ->interval .' hours ' );
312349 }
313350
314351 /**
315352 * Does the processing for advancing the iterator for daily frequency.
316353 */
317- protected function nextDaily ()
354+ protected function nextDaily ($ amount = 1 )
318355 {
319356 if (!$ this ->byHour && !$ this ->byDay ) {
320- $ this ->currentDate = $ this ->currentDate ->modify ('+ ' .$ this ->interval .' days ' );
321-
357+ $ this ->currentDate = $ this ->currentDate ->modify ('+ ' .$ amount * $ this ->interval .' days ' );
322358 return ;
323359 }
324360
@@ -338,14 +374,17 @@ protected function nextDaily()
338374 if ($ this ->byHour ) {
339375 if ('23 ' == $ this ->currentDate ->format ('G ' )) {
340376 // to obey the interval rule
341- $ this ->currentDate = $ this ->currentDate ->modify ('+ ' .$ this ->interval - 1 .' days ' );
377+ $ this ->currentDate = $ this ->currentDate ->modify ('+ ' .(($ amount * $ this ->interval ) - 1 ).' days ' );
378+ $ amount = 1 ;
342379 }
343380
344381 $ this ->currentDate = $ this ->currentDate ->modify ('+1 hours ' );
345382 } else {
346- $ this ->currentDate = $ this ->currentDate ->modify ('+ ' .$ this ->interval .' days ' );
383+ $ this ->currentDate = $ this ->currentDate ->modify ('+ ' .($ amount * $ this ->interval ).' days ' );
384+ $ amount = 1 ;
347385 }
348386
387+
349388 // Current month of the year
350389 $ currentMonth = $ this ->currentDate ->format ('n ' );
351390
@@ -364,11 +403,10 @@ protected function nextDaily()
364403 /**
365404 * Does the processing for advancing the iterator for weekly frequency.
366405 */
367- protected function nextWeekly ()
368- {
369- if (!$ this ->byHour && !$ this ->byDay ) {
370- $ this ->currentDate = $ this ->currentDate ->modify ('+ ' .$ this ->interval .' weeks ' );
406+ protected function nextWeekly ($ amount = 1 ) {
371407
408+ if (!$ this ->byHour && !$ this ->byDay ) {
409+ $ this ->currentDate = $ this ->currentDate ->modify ('+ ' .($ amount * $ this ->interval ).' weeks ' );
372410 return ;
373411 }
374412
@@ -397,9 +435,9 @@ protected function nextWeekly()
397435 $ currentHour = (int ) $ this ->currentDate ->format ('G ' );
398436
399437 // We need to roll over to the next week
400- if ($ currentDay === $ firstDay && (!$ this ->byHour || ' 0 ' == $ currentHour )) {
401- $ this ->currentDate = $ this ->currentDate ->modify ('+ ' .$ this ->interval - 1 .' weeks ' );
402-
438+ if ($ currentDay === $ firstDay && (!$ this ->byHour || $ currentHour == ' 0 ' )) {
439+ $ this ->currentDate = $ this ->currentDate ->modify ('+ ' .(( $ amount * $ this ->interval ) - 1 ) .' weeks ' );
440+ $ amount = 1 ;
403441 // We need to go to the first day of this week, but only if we
404442 // are not already on this first day of this week.
405443 if ($ this ->currentDate ->format ('w ' ) != $ firstDay ) {
@@ -414,7 +452,7 @@ protected function nextWeekly()
414452 /**
415453 * Does the processing for advancing the iterator for monthly frequency.
416454 */
417- protected function nextMonthly ()
455+ protected function nextMonthly ($ amount = 1 )
418456 {
419457 $ currentDayOfMonth = $ this ->currentDate ->format ('j ' );
420458 $ currentHourOfMonth = $ this ->currentDate ->format ('G ' );
@@ -425,9 +463,9 @@ protected function nextMonthly()
425463 // occur to the next month. We Must skip these invalid
426464 // entries.
427465 if ($ currentDayOfMonth < 29 ) {
428- $ this ->currentDate = $ this ->currentDate ->modify ('+ ' .$ this ->interval .' months ' );
466+ $ this ->currentDate = $ this ->currentDate ->modify ('+ ' .( $ amount * $ this ->interval ) .' months ' );
429467 } else {
430- $ increase = 0 ;
468+ $ increase = $ amount - 1 ;
431469 do {
432470 ++$ increase ;
433471 $ tempDate = clone $ this ->currentDate ;
@@ -443,7 +481,7 @@ protected function nextMonthly()
443481 $ occurrences = $ this ->getMonthlyOccurrences ();
444482
445483 foreach ($ occurrences as $ occurrence ) {
446- // The first occurrence thats higher than the current
484+ // The first occurrence that's higher than the current
447485 // day of the month wins.
448486 if ($ occurrence [0 ] > $ currentDayOfMonth ) {
449487 break 2 ;
@@ -469,13 +507,14 @@ protected function nextMonthly()
469507 // If we made it all the way here, it means there were no
470508 // valid occurrences, and we need to advance to the next
471509 // month.
472- //
473- // This line does not currently work in hhvm. Temporary workaround
474- // follows:
475- // $this->currentDate->modify('first day of this month');
476- $ this -> currentDate = new DateTimeImmutable ( $ this -> currentDate -> format ( ' Y-m-1 H:i:s ' ), $ this -> currentDate -> getTimezone () );
510+ $ this -> currentDate = $ this -> currentDate -> setDate (
511+ ( int ) $ this -> currentDate -> format ( ' Y ' ),
512+ ( int ) $ this -> currentDate -> format ( ' n ' ),
513+ 1
514+ );
477515 // end of workaround
478- $ this ->currentDate = $ this ->currentDate ->modify ('+ ' .$ this ->interval .' months ' );
516+ $ this ->currentDate = $ this ->currentDate ->modify ('+ ' .($ amount * $ this ->interval ).' months ' );
517+ $ amount = 1 ;
479518
480519 // This goes to 0 because we need to start counting at the
481520 // beginning.
@@ -495,10 +534,10 @@ protected function nextMonthly()
495534 /**
496535 * Does the processing for advancing the iterator for yearly frequency.
497536 */
498- protected function nextYearly ()
537+ protected function nextYearly ($ amount = 1 )
499538 {
500- $ currentMonth = $ this ->currentDate ->format ('n ' );
501539 $ currentYear = $ this ->currentDate ->format ('Y ' );
540+ $ currentMonth = $ this ->currentDate ->format ('n ' );
502541 $ currentDayOfMonth = $ this ->currentDate ->format ('j ' );
503542 $ currentHourOfMonth = $ this ->currentDate ->format ('G ' );
504543 $ currentMinuteOfMonth = $ this ->currentDate ->format ('i ' );
@@ -562,7 +601,8 @@ protected function nextYearly()
562601 }
563602
564603 // if there is no date found, check the next year
565- $ currentYear += $ this ->interval ;
604+ $ currentYear += $ amount * $ this ->interval ;
605+ $ amount = 1 ;
566606 }
567607 }
568608
@@ -604,12 +644,13 @@ protected function nextYearly()
604644 }
605645
606646 // if there is no date found, check the next year
607- $ currentYear += $ this ->interval ;
647+ $ currentYear += ($ amount * $ this ->interval );
648+ $ amount = 1 ;
608649 }
609650 }
610651
611652 // The easiest form
612- $ this ->currentDate = $ this ->currentDate ->modify ('+ ' .$ this ->interval .' years ' );
653+ $ this ->currentDate = $ this ->currentDate ->modify ('+ ' .( $ amount * $ this ->interval ) .' years ' );
613654
614655 return ;
615656 }
@@ -656,7 +697,8 @@ protected function nextYearly()
656697 do {
657698 ++$ currentMonth ;
658699 if ($ currentMonth > 12 ) {
659- $ currentYear += $ this ->interval ;
700+ $ currentYear += ($ amount * $ this ->interval );
701+ $ amount = 1 ;
660702 $ currentMonth = 1 ;
661703 }
662704 } while (!in_array ($ currentMonth , $ this ->byMonth ));
0 commit comments