1<?php
2
3namespace Sabre\VObject\Recur;
4
5use DateTime;
6use DateTimeZone;
7
8class RRuleIteratorTest extends \PHPUnit_Framework_TestCase {
9
10    function testHourly() {
11
12        $this->parse(
13            'FREQ=HOURLY;INTERVAL=3;COUNT=12',
14            '2011-10-07 12:00:00',
15            [
16                '2011-10-07 12:00:00',
17                '2011-10-07 15:00:00',
18                '2011-10-07 18:00:00',
19                '2011-10-07 21:00:00',
20                '2011-10-08 00:00:00',
21                '2011-10-08 03:00:00',
22                '2011-10-08 06:00:00',
23                '2011-10-08 09:00:00',
24                '2011-10-08 12:00:00',
25                '2011-10-08 15:00:00',
26                '2011-10-08 18:00:00',
27                '2011-10-08 21:00:00',
28            ]
29        );
30
31    }
32
33    function testDaily() {
34
35        $this->parse(
36            'FREQ=DAILY;INTERVAL=3;UNTIL=20111025T000000Z',
37            '2011-10-07',
38            [
39                '2011-10-07 00:00:00',
40                '2011-10-10 00:00:00',
41                '2011-10-13 00:00:00',
42                '2011-10-16 00:00:00',
43                '2011-10-19 00:00:00',
44                '2011-10-22 00:00:00',
45                '2011-10-25 00:00:00',
46            ]
47        );
48
49    }
50
51    function testDailyByDayByHour() {
52
53        $this->parse(
54            'FREQ=DAILY;BYDAY=SA,SU;BYHOUR=6,7',
55            '2011-10-08 06:00:00',
56            [
57                '2011-10-08 06:00:00',
58                '2011-10-08 07:00:00',
59                '2011-10-09 06:00:00',
60                '2011-10-09 07:00:00',
61                '2011-10-15 06:00:00',
62                '2011-10-15 07:00:00',
63                '2011-10-16 06:00:00',
64                '2011-10-16 07:00:00',
65                '2011-10-22 06:00:00',
66                '2011-10-22 07:00:00',
67                '2011-10-23 06:00:00',
68                '2011-10-23 07:00:00',
69            ]
70        );
71
72    }
73
74    function testDailyByHour() {
75
76        $this->parse(
77            'FREQ=DAILY;INTERVAL=2;BYHOUR=10,11,12,13,14,15',
78            '2012-10-11 12:00:00',
79            [
80                '2012-10-11 12:00:00',
81                '2012-10-11 13:00:00',
82                '2012-10-11 14:00:00',
83                '2012-10-11 15:00:00',
84                '2012-10-13 10:00:00',
85                '2012-10-13 11:00:00',
86                '2012-10-13 12:00:00',
87                '2012-10-13 13:00:00',
88                '2012-10-13 14:00:00',
89                '2012-10-13 15:00:00',
90                '2012-10-15 10:00:00',
91                '2012-10-15 11:00:00',
92            ]
93        );
94
95    }
96
97    function testDailyByDay() {
98
99        $this->parse(
100            'FREQ=DAILY;INTERVAL=2;BYDAY=TU,WE,FR',
101            '2011-10-07 12:00:00',
102            [
103                '2011-10-07 12:00:00',
104                '2011-10-11 12:00:00',
105                '2011-10-19 12:00:00',
106                '2011-10-21 12:00:00',
107                '2011-10-25 12:00:00',
108                '2011-11-02 12:00:00',
109                '2011-11-04 12:00:00',
110                '2011-11-08 12:00:00',
111                '2011-11-16 12:00:00',
112                '2011-11-18 12:00:00',
113                '2011-11-22 12:00:00',
114                '2011-11-30 12:00:00',
115            ]
116        );
117
118    }
119
120    function testDailyCount() {
121
122        $this->parse(
123            'FREQ=DAILY;COUNT=5',
124            '2014-08-01 18:03:00',
125            [
126                '2014-08-01 18:03:00',
127                '2014-08-02 18:03:00',
128                '2014-08-03 18:03:00',
129                '2014-08-04 18:03:00',
130                '2014-08-05 18:03:00',
131            ]
132        );
133
134    }
135
136    function testDailyByMonth() {
137
138        $this->parse(
139            'FREQ=DAILY;BYMONTH=9,10;BYDAY=SU',
140            '2007-10-04 16:00:00',
141            [
142                '2013-09-29 16:00:00',
143                '2013-10-06 16:00:00',
144                '2013-10-13 16:00:00',
145                '2013-10-20 16:00:00',
146                '2013-10-27 16:00:00',
147                '2014-09-07 16:00:00'
148            ],
149            '2013-09-28'
150        );
151
152    }
153
154    function testWeekly() {
155
156        $this->parse(
157            'FREQ=WEEKLY;INTERVAL=2;COUNT=10',
158            '2011-10-07 00:00:00',
159            [
160                '2011-10-07 00:00:00',
161                '2011-10-21 00:00:00',
162                '2011-11-04 00:00:00',
163                '2011-11-18 00:00:00',
164                '2011-12-02 00:00:00',
165                '2011-12-16 00:00:00',
166                '2011-12-30 00:00:00',
167                '2012-01-13 00:00:00',
168                '2012-01-27 00:00:00',
169                '2012-02-10 00:00:00',
170            ]
171        );
172
173    }
174
175    function testWeeklyByDay() {
176
177        $this->parse(
178            'FREQ=WEEKLY;INTERVAL=1;COUNT=4;BYDAY=MO;WKST=SA',
179            '2014-08-01 00:00:00',
180            [
181                '2014-08-01 00:00:00',
182                '2014-08-04 00:00:00',
183                '2014-08-11 00:00:00',
184                '2014-08-18 00:00:00',
185            ]
186        );
187
188    }
189
190    function testWeeklyByDay2() {
191
192        $this->parse(
193            'FREQ=WEEKLY;INTERVAL=2;BYDAY=TU,WE,FR;WKST=SU',
194            '2011-10-07 00:00:00',
195            [
196                '2011-10-07 00:00:00',
197                '2011-10-18 00:00:00',
198                '2011-10-19 00:00:00',
199                '2011-10-21 00:00:00',
200                '2011-11-01 00:00:00',
201                '2011-11-02 00:00:00',
202                '2011-11-04 00:00:00',
203                '2011-11-15 00:00:00',
204                '2011-11-16 00:00:00',
205                '2011-11-18 00:00:00',
206                '2011-11-29 00:00:00',
207                '2011-11-30 00:00:00',
208            ]
209        );
210
211    }
212
213    function testWeeklyByDayByHour() {
214
215        $this->parse(
216            'FREQ=WEEKLY;INTERVAL=2;BYDAY=TU,WE,FR;WKST=MO;BYHOUR=8,9,10',
217            '2011-10-07 08:00:00',
218            [
219                '2011-10-07 08:00:00',
220                '2011-10-07 09:00:00',
221                '2011-10-07 10:00:00',
222                '2011-10-18 08:00:00',
223                '2011-10-18 09:00:00',
224                '2011-10-18 10:00:00',
225                '2011-10-19 08:00:00',
226                '2011-10-19 09:00:00',
227                '2011-10-19 10:00:00',
228                '2011-10-21 08:00:00',
229                '2011-10-21 09:00:00',
230                '2011-10-21 10:00:00',
231                '2011-11-01 08:00:00',
232                '2011-11-01 09:00:00',
233                '2011-11-01 10:00:00',
234            ]
235        );
236
237    }
238
239    function testWeeklyByDaySpecificHour() {
240
241        $this->parse(
242            'FREQ=WEEKLY;INTERVAL=2;BYDAY=TU,WE,FR;WKST=SU',
243            '2011-10-07 18:00:00',
244            [
245                '2011-10-07 18:00:00',
246                '2011-10-18 18:00:00',
247                '2011-10-19 18:00:00',
248                '2011-10-21 18:00:00',
249                '2011-11-01 18:00:00',
250                '2011-11-02 18:00:00',
251                '2011-11-04 18:00:00',
252                '2011-11-15 18:00:00',
253                '2011-11-16 18:00:00',
254                '2011-11-18 18:00:00',
255                '2011-11-29 18:00:00',
256                '2011-11-30 18:00:00',
257            ]
258        );
259
260    }
261
262    function testMonthly() {
263
264        $this->parse(
265            'FREQ=MONTHLY;INTERVAL=3;COUNT=5',
266            '2011-12-05 00:00:00',
267            [
268                 '2011-12-05 00:00:00',
269                 '2012-03-05 00:00:00',
270                 '2012-06-05 00:00:00',
271                 '2012-09-05 00:00:00',
272                 '2012-12-05 00:00:00',
273            ]
274        );
275
276    }
277
278    function testMonlthyEndOfMonth() {
279
280        $this->parse(
281            'FREQ=MONTHLY;INTERVAL=2;COUNT=12',
282            '2011-12-31 00:00:00',
283            [
284                '2011-12-31 00:00:00',
285                '2012-08-31 00:00:00',
286                '2012-10-31 00:00:00',
287                '2012-12-31 00:00:00',
288                '2013-08-31 00:00:00',
289                '2013-10-31 00:00:00',
290                '2013-12-31 00:00:00',
291                '2014-08-31 00:00:00',
292                '2014-10-31 00:00:00',
293                '2014-12-31 00:00:00',
294                '2015-08-31 00:00:00',
295                '2015-10-31 00:00:00',
296            ]
297        );
298
299    }
300
301    function testMonthlyByMonthDay() {
302
303        $this->parse(
304            'FREQ=MONTHLY;INTERVAL=5;COUNT=9;BYMONTHDAY=1,31,-7',
305            '2011-01-01 00:00:00',
306            [
307                '2011-01-01 00:00:00',
308                '2011-01-25 00:00:00',
309                '2011-01-31 00:00:00',
310                '2011-06-01 00:00:00',
311                '2011-06-24 00:00:00',
312                '2011-11-01 00:00:00',
313                '2011-11-24 00:00:00',
314                '2012-04-01 00:00:00',
315                '2012-04-24 00:00:00',
316            ]
317        );
318
319    }
320
321    function testMonthlyByDay() {
322
323        $this->parse(
324            'FREQ=MONTHLY;INTERVAL=2;COUNT=16;BYDAY=MO,-2TU,+1WE,3TH',
325            '2011-01-03 00:00:00',
326            [
327                '2011-01-03 00:00:00',
328                '2011-01-05 00:00:00',
329                '2011-01-10 00:00:00',
330                '2011-01-17 00:00:00',
331                '2011-01-18 00:00:00',
332                '2011-01-20 00:00:00',
333                '2011-01-24 00:00:00',
334                '2011-01-31 00:00:00',
335                '2011-03-02 00:00:00',
336                '2011-03-07 00:00:00',
337                '2011-03-14 00:00:00',
338                '2011-03-17 00:00:00',
339                '2011-03-21 00:00:00',
340                '2011-03-22 00:00:00',
341                '2011-03-28 00:00:00',
342                '2011-05-02 00:00:00',
343            ]
344        );
345
346    }
347
348    function testMonthlyByDayByMonthDay() {
349
350        $this->parse(
351            'FREQ=MONTHLY;COUNT=10;BYDAY=MO;BYMONTHDAY=1',
352            '2011-08-01 00:00:00',
353            [
354                '2011-08-01 00:00:00',
355                '2012-10-01 00:00:00',
356                '2013-04-01 00:00:00',
357                '2013-07-01 00:00:00',
358                '2014-09-01 00:00:00',
359                '2014-12-01 00:00:00',
360                '2015-06-01 00:00:00',
361                '2016-02-01 00:00:00',
362                '2016-08-01 00:00:00',
363                '2017-05-01 00:00:00',
364            ]
365        );
366
367    }
368
369    function testMonthlyByDayBySetPos() {
370
371        $this->parse(
372            'FREQ=MONTHLY;COUNT=10;BYDAY=MO,TU,WE,TH,FR;BYSETPOS=1,-1',
373            '2011-01-03 00:00:00',
374            [
375                '2011-01-03 00:00:00',
376                '2011-01-31 00:00:00',
377                '2011-02-01 00:00:00',
378                '2011-02-28 00:00:00',
379                '2011-03-01 00:00:00',
380                '2011-03-31 00:00:00',
381                '2011-04-01 00:00:00',
382                '2011-04-29 00:00:00',
383                '2011-05-02 00:00:00',
384                '2011-05-31 00:00:00',
385            ]
386        );
387
388    }
389
390    function testYearly() {
391
392        $this->parse(
393            'FREQ=YEARLY;COUNT=10;INTERVAL=3',
394            '2011-01-01 00:00:00',
395            [
396                '2011-01-01 00:00:00',
397                '2014-01-01 00:00:00',
398                '2017-01-01 00:00:00',
399                '2020-01-01 00:00:00',
400                '2023-01-01 00:00:00',
401                '2026-01-01 00:00:00',
402                '2029-01-01 00:00:00',
403                '2032-01-01 00:00:00',
404                '2035-01-01 00:00:00',
405                '2038-01-01 00:00:00',
406            ]
407        );
408    }
409
410    function testYearlyLeapYear() {
411
412        $this->parse(
413            'FREQ=YEARLY;COUNT=3',
414            '2012-02-29 00:00:00',
415            [
416                '2012-02-29 00:00:00',
417                '2016-02-29 00:00:00',
418                '2020-02-29 00:00:00',
419            ]
420        );
421    }
422
423    function testYearlyByMonth() {
424
425        $this->parse(
426            'FREQ=YEARLY;COUNT=8;INTERVAL=4;BYMONTH=4,10',
427            '2011-04-07 00:00:00',
428            [
429                '2011-04-07 00:00:00',
430                '2011-10-07 00:00:00',
431                '2015-04-07 00:00:00',
432                '2015-10-07 00:00:00',
433                '2019-04-07 00:00:00',
434                '2019-10-07 00:00:00',
435                '2023-04-07 00:00:00',
436                '2023-10-07 00:00:00',
437            ]
438        );
439
440    }
441
442    /**
443     * @expectedException \Sabre\VObject\InvalidDataException
444     */
445    function testYearlyByMonthInvalidValue1() {
446
447        $this->parse(
448            'FREQ=YEARLY;COUNT=6;BYMONTHDAY=24;BYMONTH=0',
449            '2011-04-07 00:00:00',
450            []
451        );
452
453    }
454
455    /**
456     * @expectedException \Sabre\VObject\InvalidDataException
457     */
458    function testYearlyByMonthInvalidValue2() {
459
460        $this->parse(
461            'FREQ=YEARLY;COUNT=6;BYMONTHDAY=24;BYMONTH=bla',
462            '2011-04-07 00:00:00',
463            []
464        );
465
466    }
467
468    /**
469     * @expectedException \Sabre\VObject\InvalidDataException
470     */
471    function testYearlyByMonthManyInvalidValues() {
472
473        $this->parse(
474            'FREQ=YEARLY;COUNT=6;BYMONTHDAY=24;BYMONTH=0,bla',
475            '2011-04-07 00:00:00',
476            []
477        );
478
479    }
480
481    /**
482     * @expectedException \Sabre\VObject\InvalidDataException
483     */
484    function testYearlyByMonthEmptyValue() {
485
486        $this->parse(
487            'FREQ=YEARLY;COUNT=6;BYMONTHDAY=24;BYMONTH=',
488            '2011-04-07 00:00:00',
489            []
490        );
491
492    }
493
494    function testYearlyByMonthByDay() {
495
496        $this->parse(
497            'FREQ=YEARLY;COUNT=8;INTERVAL=5;BYMONTH=4,10;BYDAY=1MO,-1SU',
498            '2011-04-04 00:00:00',
499            [
500                '2011-04-04 00:00:00',
501                '2011-04-24 00:00:00',
502                '2011-10-03 00:00:00',
503                '2011-10-30 00:00:00',
504                '2016-04-04 00:00:00',
505                '2016-04-24 00:00:00',
506                '2016-10-03 00:00:00',
507                '2016-10-30 00:00:00',
508            ]
509        );
510
511    }
512
513    function testYearlyByYearDay() {
514
515        $this->parse(
516            'FREQ=YEARLY;COUNT=7;INTERVAL=2;BYYEARDAY=190',
517            '2011-07-10 03:07:00',
518            [
519                '2011-07-10 03:07:00',
520                '2013-07-10 03:07:00',
521                '2015-07-10 03:07:00',
522                '2017-07-10 03:07:00',
523                '2019-07-10 03:07:00',
524                '2021-07-10 03:07:00',
525                '2023-07-10 03:07:00',
526            ]
527        );
528
529    }
530
531    function testYearlyByYearDayMultiple() {
532
533        $this->parse(
534            'FREQ=YEARLY;COUNT=8;INTERVAL=3;BYYEARDAY=190,301',
535            '2011-07-10 14:53:11',
536            [
537                '2011-07-10 14:53:11',
538                '2011-10-29 14:53:11',
539                '2014-07-10 14:53:11',
540                '2014-10-29 14:53:11',
541                '2017-07-10 14:53:11',
542                '2017-10-29 14:53:11',
543                '2020-07-09 14:53:11',
544                '2020-10-28 14:53:11',
545            ]
546        );
547
548    }
549
550    function testYearlyByYearDayByDay() {
551
552        $this->parse(
553            'FREQ=YEARLY;COUNT=6;BYYEARDAY=97;BYDAY=SA',
554            '2001-04-07 14:53:11',
555            [
556                '2001-04-07 14:53:11',
557                '2006-04-08 14:53:11',
558                '2012-04-07 14:53:11',
559                '2017-04-08 14:53:11',
560                '2023-04-08 14:53:11',
561                '2034-04-08 14:53:11',
562            ]
563        );
564
565    }
566
567    function testYearlyByYearDayNegative() {
568
569        $this->parse(
570            'FREQ=YEARLY;COUNT=8;BYYEARDAY=-97,-5',
571            '2001-09-26 14:53:11',
572            [
573                '2001-09-26 14:53:11',
574                '2001-12-27 14:53:11',
575                '2002-09-26 14:53:11',
576                '2002-12-27 14:53:11',
577                '2003-09-26 14:53:11',
578                '2003-12-27 14:53:11',
579                '2004-09-26 14:53:11',
580                '2004-12-27 14:53:11',
581            ]
582        );
583
584    }
585
586    /**
587     * @expectedException \Sabre\VObject\InvalidDataException
588     */
589    function testYearlyByYearDayInvalid390() {
590
591        $this->parse(
592            'FREQ=YEARLY;COUNT=8;INTERVAL=4;BYYEARDAY=390',
593            '2011-04-07 00:00:00',
594            [
595            ]
596        );
597
598    }
599
600    /**
601     * @expectedException \Sabre\VObject\InvalidDataException
602     */
603    function testYearlyByYearDayInvalid0() {
604
605        $this->parse(
606            'FREQ=YEARLY;COUNT=8;INTERVAL=4;BYYEARDAY=0',
607            '2011-04-07 00:00:00',
608            [
609            ]
610        );
611
612    }
613
614    function testFastForward() {
615
616        // The idea is that we're fast-forwarding too far in the future, so
617        // there will be no results left.
618        $this->parse(
619            'FREQ=YEARLY;COUNT=8;INTERVAL=5;BYMONTH=4,10;BYDAY=1MO,-1SU',
620            '2011-04-04 00:00:00',
621            [],
622            '2020-05-05 00:00:00'
623        );
624
625    }
626
627    /**
628     * The bug that was in the
629     * system before would fail on the 5th tuesday of the month, if the 5th
630     * tuesday did not exist.
631     *
632     * A pretty slow test. Had to be marked as 'medium' for phpunit to not die
633     * after 1 second. Would be good to optimize later.
634     *
635     * @medium
636     */
637    function testFifthTuesdayProblem() {
638
639        $this->parse(
640            'FREQ=MONTHLY;INTERVAL=1;UNTIL=20071030T035959Z;BYDAY=5TU',
641            '2007-10-04 14:46:42',
642            [
643                '2007-10-04 14:46:42',
644            ]
645        );
646
647    }
648
649    /**
650     * This bug came from a Fruux customer. This would result in a never-ending
651     * request.
652     */
653    function testFastFowardTooFar() {
654
655        $this->parse(
656            'FREQ=WEEKLY;BYDAY=MO;UNTIL=20090704T205959Z;INTERVAL=1',
657            '2009-04-20 18:00:00',
658            [
659                '2009-04-20 18:00:00',
660                '2009-04-27 18:00:00',
661                '2009-05-04 18:00:00',
662                '2009-05-11 18:00:00',
663                '2009-05-18 18:00:00',
664                '2009-05-25 18:00:00',
665                '2009-06-01 18:00:00',
666                '2009-06-08 18:00:00',
667                '2009-06-15 18:00:00',
668                '2009-06-22 18:00:00',
669                '2009-06-29 18:00:00',
670            ]
671        );
672
673    }
674
675    function testValidByWeekNo() {
676
677        $this->parse(
678            'FREQ=YEARLY;BYWEEKNO=20;BYDAY=TU',
679            '2011-02-07 00:00:00',
680            [
681                '2011-02-07 00:00:00',
682                '2011-05-17 00:00:00',
683                '2012-05-15 00:00:00',
684                '2013-05-14 00:00:00',
685                '2014-05-13 00:00:00',
686                '2015-05-12 00:00:00',
687                '2016-05-17 00:00:00',
688                '2017-05-16 00:00:00',
689                '2018-05-15 00:00:00',
690                '2019-05-14 00:00:00',
691                '2020-05-12 00:00:00',
692                '2021-05-18 00:00:00',
693            ]
694        );
695
696    }
697
698    function testNegativeValidByWeekNo() {
699
700        $this->parse(
701            'FREQ=YEARLY;BYWEEKNO=-20;BYDAY=TU,FR',
702            '2011-09-02 00:00:00',
703            [
704                '2011-09-02 00:00:00',
705                '2012-08-07 00:00:00',
706                '2012-08-10 00:00:00',
707                '2013-08-06 00:00:00',
708                '2013-08-09 00:00:00',
709                '2014-08-05 00:00:00',
710                '2014-08-08 00:00:00',
711                '2015-08-11 00:00:00',
712                '2015-08-14 00:00:00',
713                '2016-08-09 00:00:00',
714                '2016-08-12 00:00:00',
715                '2017-08-08 00:00:00',
716            ]
717        );
718
719    }
720
721    function testTwoValidByWeekNo() {
722
723        $this->parse(
724            'FREQ=YEARLY;BYWEEKNO=20;BYDAY=TU,FR',
725            '2011-09-07 09:00:00',
726            [
727                '2011-09-07 09:00:00',
728                '2012-05-15 09:00:00',
729                '2012-05-18 09:00:00',
730                '2013-05-14 09:00:00',
731                '2013-05-17 09:00:00',
732                '2014-05-13 09:00:00',
733                '2014-05-16 09:00:00',
734                '2015-05-12 09:00:00',
735                '2015-05-15 09:00:00',
736                '2016-05-17 09:00:00',
737                '2016-05-20 09:00:00',
738                '2017-05-16 09:00:00',
739            ]
740        );
741
742    }
743
744    function testValidByWeekNoByDayDefault() {
745
746        $this->parse(
747            'FREQ=YEARLY;BYWEEKNO=20',
748            '2011-05-16 00:00:00',
749            [
750                '2011-05-16 00:00:00',
751                '2012-05-14 00:00:00',
752                '2013-05-13 00:00:00',
753                '2014-05-12 00:00:00',
754                '2015-05-11 00:00:00',
755                '2016-05-16 00:00:00',
756                '2017-05-15 00:00:00',
757                '2018-05-14 00:00:00',
758                '2019-05-13 00:00:00',
759                '2020-05-11 00:00:00',
760                '2021-05-17 00:00:00',
761                '2022-05-16 00:00:00',
762            ]
763        );
764
765    }
766
767    function testMultipleValidByWeekNo() {
768
769        $this->parse(
770            'FREQ=YEARLY;BYWEEKNO=20,50;BYDAY=TU,FR',
771            '2011-01-16 00:00:00',
772            [
773                '2011-01-16 00:00:00',
774                '2011-05-17 00:00:00',
775                '2011-05-20 00:00:00',
776                '2011-12-13 00:00:00',
777                '2011-12-16 00:00:00',
778                '2012-05-15 00:00:00',
779                '2012-05-18 00:00:00',
780                '2012-12-11 00:00:00',
781                '2012-12-14 00:00:00',
782                '2013-05-14 00:00:00',
783                '2013-05-17 00:00:00',
784                '2013-12-10 00:00:00',
785            ]
786        );
787
788    }
789
790    /**
791     * @expectedException \Sabre\VObject\InvalidDataException
792     */
793    function testInvalidByWeekNo() {
794
795        $this->parse(
796            'FREQ=YEARLY;BYWEEKNO=54',
797            '2011-05-16 00:00:00',
798            [
799            ]
800        );
801
802    }
803
804    /**
805     * This also at one point caused an infinite loop. We're keeping the test.
806     */
807    function testYearlyByMonthLoop() {
808
809        $this->parse(
810            'FREQ=YEARLY;INTERVAL=1;UNTIL=20120203T225959Z;BYMONTH=2;BYSETPOS=1;BYDAY=SU,MO,TU,WE,TH,FR,SA',
811            '2012-01-01 15:45:00',
812            [
813                '2012-02-01 15:45:00',
814            ],
815            '2012-01-29 23:00:00'
816        );
817
818
819    }
820
821    /**
822     * Something, somewhere produced an ics with an interval set to 0. Because
823     * this means we increase the current day (or week, month) by 0, this also
824     * results in an infinite loop.
825     *
826     * @expectedException \Sabre\VObject\InvalidDataException
827     */
828    function testZeroInterval() {
829
830        $this->parse(
831            'FREQ=YEARLY;INTERVAL=0',
832            '2012-08-24 14:57:00',
833            [],
834            '2013-01-01 23:00:00'
835        );
836
837    }
838
839    /**
840     * @expectedException \Sabre\VObject\InvalidDataException
841     */
842    function testInvalidFreq() {
843
844        $this->parse(
845            'FREQ=SMONTHLY;INTERVAL=3;UNTIL=20111025T000000Z',
846            '2011-10-07',
847            []
848        );
849
850    }
851
852    /**
853     * @expectedException \Sabre\VObject\InvalidDataException
854     */
855    function testByDayBadOffset() {
856
857        $this->parse(
858            'FREQ=WEEKLY;INTERVAL=1;COUNT=4;BYDAY=0MO;WKST=SA',
859            '2014-08-01 00:00:00',
860            []
861        );
862
863    }
864
865    function testUntilBeginHasTimezone() {
866
867        $this->parse(
868            'FREQ=WEEKLY;UNTIL=20131118T183000',
869            '2013-09-23 18:30:00',
870            [
871                '2013-09-23 18:30:00',
872                '2013-09-30 18:30:00',
873                '2013-10-07 18:30:00',
874                '2013-10-14 18:30:00',
875                '2013-10-21 18:30:00',
876                '2013-10-28 18:30:00',
877                '2013-11-04 18:30:00',
878                '2013-11-11 18:30:00',
879                '2013-11-18 18:30:00',
880            ],
881            null,
882            'America/New_York'
883        );
884
885    }
886
887    function testUntilBeforeDtStart() {
888
889        $this->parse(
890            'FREQ=DAILY;UNTIL=20140101T000000Z',
891            '2014-08-02 00:15:00',
892            [
893                '2014-08-02 00:15:00',
894            ]
895        );
896
897    }
898
899    function testIgnoredStuff() {
900
901        $this->parse(
902            'FREQ=DAILY;BYSECOND=1;BYMINUTE=1;BYYEARDAY=1;BYWEEKNO=1;COUNT=2',
903            '2014-08-02 00:15:00',
904            [
905                '2014-08-02 00:15:00',
906                '2014-08-03 00:15:00',
907            ]
908        );
909
910    }
911
912    function testMinusFifthThursday() {
913
914        $this->parse(
915            'FREQ=MONTHLY;BYDAY=-4TH,-5TH;COUNT=4',
916            '2015-01-01 00:15:00',
917            [
918                '2015-01-01 00:15:00',
919                '2015-01-08 00:15:00',
920                '2015-02-05 00:15:00',
921                '2015-03-05 00:15:00'
922            ]
923        );
924
925    }
926
927    /**
928     * @expectedException \Sabre\VObject\InvalidDataException
929     */
930    function testUnsupportedPart() {
931
932        $this->parse(
933            'FREQ=DAILY;BYWODAN=1',
934            '2014-08-02 00:15:00',
935            []
936        );
937
938    }
939
940    function testIteratorFunctions() {
941
942        $parser = new RRuleIterator('FREQ=DAILY', new DateTime('2014-08-02 00:00:13'));
943        $parser->next();
944        $this->assertEquals(
945            new DateTime('2014-08-03 00:00:13'),
946            $parser->current()
947        );
948        $this->assertEquals(
949            1,
950            $parser->key()
951        );
952
953        $parser->rewind();
954
955        $this->assertEquals(
956            new DateTime('2014-08-02 00:00:13'),
957            $parser->current()
958        );
959        $this->assertEquals(
960            0,
961            $parser->key()
962        );
963
964    }
965
966    function parse($rule, $start, $expected, $fastForward = null, $tz = 'UTC') {
967
968        $dt = new DateTime($start, new DateTimeZone($tz));
969        $parser = new RRuleIterator($rule, $dt);
970
971        if ($fastForward) {
972            $parser->fastForward(new DateTime($fastForward));
973        }
974
975        $result = [];
976        while ($parser->valid()) {
977
978            $item = $parser->current();
979            $result[] = $item->format('Y-m-d H:i:s');
980
981            if ($parser->isInfinite() && count($result) >= count($expected)) {
982                break;
983            }
984            $parser->next();
985
986        }
987
988        $this->assertEquals(
989            $expected,
990            $result
991        );
992
993    }
994
995}
996