1<?php
2
3namespace Sabre\VObject\Recur\EventIterator;
4
5use DateTime;
6use DateTimeZone;
7use Sabre\VObject\Recur\EventIterator;
8use Sabre\VObject\Component\VCalendar;
9
10class MainTest extends \PHPUnit_Framework_TestCase {
11
12    function testValues() {
13
14        $vcal = new VCalendar();
15        $ev = $vcal->createComponent('VEVENT');
16        $ev->UID = 'bla';
17        $ev->RRULE = 'FREQ=DAILY;BYHOUR=10;BYMINUTE=5;BYSECOND=16;BYWEEKNO=32;BYYEARDAY=100,200';
18        $dtStart = $vcal->createProperty('DTSTART');
19        $dtStart->setDateTime(new DateTime('2011-10-07'));
20
21        $ev->add($dtStart);
22
23        $vcal->add($ev);
24
25        $it = new EventIterator($vcal,(string)$ev->uid);
26
27        $this->assertTrue($it->isInfinite());
28
29    }
30
31    /**
32     * @expectedException InvalidArgumentException
33     * @depends testValues
34     */
35    function testInvalidFreq() {
36
37        $vcal = new VCalendar();
38        $ev = $vcal->createComponent('VEVENT');
39        $ev->RRULE = 'FREQ=SMONTHLY;INTERVAL=3;UNTIL=20111025T000000Z';
40        $ev->UID = 'foo';
41        $dtStart = $vcal->createProperty('DTSTART');
42        $dtStart->setDateTime(new DateTime('2011-10-07', new DateTimeZone('UTC')));
43
44        $ev->add($dtStart);
45        $vcal->add($ev);
46
47        $it = new EventIterator($vcal,(string)$ev->uid);
48
49    }
50
51    /**
52     * @expectedException InvalidArgumentException
53     */
54    function testVCalendarNoUID() {
55
56        $vcal = new VCalendar();
57        $it = new EventIterator($vcal);
58
59    }
60
61    /**
62     * @expectedException InvalidArgumentException
63     */
64    function testVCalendarInvalidUID() {
65
66        $vcal = new VCalendar();
67        $it = new EventIterator($vcal,'foo');
68
69    }
70
71    /**
72     * @depends testValues
73     */
74    function testHourly() {
75
76        $vcal = new VCalendar();
77        $ev = $vcal->createComponent('VEVENT');
78
79        $ev->UID = 'bla';
80        $ev->RRULE = 'FREQ=HOURLY;INTERVAL=3;UNTIL=20111025T000000Z';
81        $dtStart = $vcal->createProperty('DTSTART');
82        $dtStart->setDateTime(new DateTime('2011-10-07 12:00:00', new DateTimeZone('UTC')));
83
84        $ev->add($dtStart);
85        $vcal->add($ev);
86
87        $it = new EventIterator($vcal,$ev->uid);
88
89        // Max is to prevent overflow
90        $max = 12;
91        $result = array();
92        foreach($it as $item) {
93
94            $result[] = $item;
95            $max--;
96
97            if (!$max) break;
98
99        }
100
101        $tz = new DateTimeZone('UTC');
102
103        $this->assertEquals(
104            array(
105                new DateTime('2011-10-07 12:00:00', $tz),
106                new DateTime('2011-10-07 15:00:00', $tz),
107                new DateTime('2011-10-07 18:00:00', $tz),
108                new DateTime('2011-10-07 21:00:00', $tz),
109                new DateTime('2011-10-08 00:00:00', $tz),
110                new DateTime('2011-10-08 03:00:00', $tz),
111                new DateTime('2011-10-08 06:00:00', $tz),
112                new DateTime('2011-10-08 09:00:00', $tz),
113                new DateTime('2011-10-08 12:00:00', $tz),
114                new DateTime('2011-10-08 15:00:00', $tz),
115                new DateTime('2011-10-08 18:00:00', $tz),
116                new DateTime('2011-10-08 21:00:00', $tz),
117            ),
118            $result
119        );
120
121    }
122
123    /**
124     * @depends testValues
125     */
126    function testDaily() {
127
128        $vcal = new VCalendar();
129        $ev = $vcal->createComponent('VEVENT');
130
131        $ev->UID = 'bla';
132        $ev->RRULE = 'FREQ=DAILY;INTERVAL=3;UNTIL=20111025T000000Z';
133        $dtStart = $vcal->createProperty('DTSTART');
134        $dtStart->setDateTime(new DateTime('2011-10-07', new DateTimeZone('UTC')));
135
136        $ev->add($dtStart);
137
138        $vcal->add($ev);
139
140        $it = new EventIterator($vcal,$ev->uid);
141
142        // Max is to prevent overflow
143        $max = 12;
144        $result = array();
145        foreach($it as $item) {
146
147            $result[] = $item;
148            $max--;
149
150            if (!$max) break;
151
152        }
153
154        $tz = new DateTimeZone('UTC');
155
156        $this->assertEquals(
157            array(
158                new DateTime('2011-10-07', $tz),
159                new DateTime('2011-10-10', $tz),
160                new DateTime('2011-10-13', $tz),
161                new DateTime('2011-10-16', $tz),
162                new DateTime('2011-10-19', $tz),
163                new DateTime('2011-10-22', $tz),
164                new DateTime('2011-10-25', $tz),
165            ),
166            $result
167        );
168
169    }
170
171    /**
172     * @depends testValues
173     */
174    function testNoRRULE() {
175
176        $vcal = new VCalendar();
177        $ev = $vcal->createComponent('VEVENT');
178
179        $ev->UID = 'bla';
180        $dtStart = $vcal->createProperty('DTSTART');
181        $dtStart->setDateTime(new DateTime('2011-10-07', new DateTimeZone('UTC')));
182
183        $ev->add($dtStart);
184
185        $vcal->add($ev);
186
187        $it = new EventIterator($vcal,$ev->uid);
188
189        // Max is to prevent overflow
190        $max = 12;
191        $result = array();
192        foreach($it as $item) {
193
194            $result[] = $item;
195            $max--;
196
197            if (!$max) break;
198
199        }
200
201        $tz = new DateTimeZone('UTC');
202
203        $this->assertEquals(
204            array(
205                new DateTime('2011-10-07', $tz),
206            ),
207            $result
208        );
209
210    }
211
212    /**
213     * @depends testValues
214     */
215    function testDailyByDayByHour() {
216
217        $vcal = new VCalendar();
218        $ev = $vcal->createComponent('VEVENT');
219
220        $ev->UID = 'bla';
221        $ev->RRULE = 'FREQ=DAILY;BYDAY=SA,SU;BYHOUR=6,7';
222        $dtStart = $vcal->createProperty('DTSTART');
223        $dtStart->setDateTime(new DateTime('2011-10-08 06:00:00', new DateTimeZone('UTC')));
224
225        $ev->add($dtStart);
226
227        $vcal->add($ev);
228
229        $it = new EventIterator($vcal,(string)$ev->uid);
230
231        // Grabbing the next 12 items
232        $max = 12;
233        $result = array();
234        foreach($it as $item) {
235
236            $result[] = $item;
237            $max--;
238
239            if (!$max) break;
240
241        }
242
243        $tz = new DateTimeZone('UTC');
244
245        $this->assertEquals(
246            array(
247                new datetime('2011-10-08 06:00:00', $tz),
248                new datetime('2011-10-08 07:00:00', $tz),
249                new datetime('2011-10-09 06:00:00', $tz),
250                new datetime('2011-10-09 07:00:00', $tz),
251                new datetime('2011-10-15 06:00:00', $tz),
252                new datetime('2011-10-15 07:00:00', $tz),
253                new datetime('2011-10-16 06:00:00', $tz),
254                new datetime('2011-10-16 07:00:00', $tz),
255                new datetime('2011-10-22 06:00:00', $tz),
256                new datetime('2011-10-22 07:00:00', $tz),
257                new datetime('2011-10-23 06:00:00', $tz),
258                new datetime('2011-10-23 07:00:00', $tz),
259            ),
260            $result
261        );
262
263    }
264
265    /**
266     * @depends testValues
267     */
268    function testDailyByHour() {
269
270        $vcal = new VCalendar();
271        $ev = $vcal->createComponent('VEVENT');
272
273        $ev->UID = 'bla';
274        $ev->RRULE = 'FREQ=DAILY;INTERVAL=2;BYHOUR=10,11,12,13,14,15';
275        $dtStart = $vcal->createProperty('DTSTART');
276        $dtStart->setDateTime(new DateTime('2012-10-11 12:00:00', new DateTimeZone('UTC')));
277
278        $ev->add($dtStart);
279
280        $vcal->add($ev);
281
282        $it = new EventIterator($vcal,(string)$ev->uid);
283
284        // Grabbing the next 12 items
285        $max = 12;
286        $result = array();
287        foreach($it as $item) {
288
289            $result[] = $item;
290            $max--;
291
292            if (!$max) break;
293
294        }
295
296        $tz = new DateTimeZone('UTC');
297
298        $this->assertEquals(
299            array(
300                new datetime('2012-10-11 12:00:00', $tz),
301                new datetime('2012-10-11 13:00:00', $tz),
302                new datetime('2012-10-11 14:00:00', $tz),
303                new datetime('2012-10-11 15:00:00', $tz),
304                new datetime('2012-10-13 10:00:00', $tz),
305                new datetime('2012-10-13 11:00:00', $tz),
306                new datetime('2012-10-13 12:00:00', $tz),
307                new datetime('2012-10-13 13:00:00', $tz),
308                new datetime('2012-10-13 14:00:00', $tz),
309                new datetime('2012-10-13 15:00:00', $tz),
310                new datetime('2012-10-15 10:00:00', $tz),
311                new datetime('2012-10-15 11:00:00', $tz),
312            ),
313            $result
314        );
315
316    }
317
318    /**
319     * @depends testValues
320     */
321    function testDailyByDay() {
322
323        $vcal = new VCalendar();
324        $ev = $vcal->createComponent('VEVENT');
325
326        $ev->UID = 'bla';
327        $ev->RRULE = 'FREQ=DAILY;INTERVAL=2;BYDAY=TU,WE,FR';
328        $dtStart = $vcal->createProperty('DTSTART');
329        $dtStart->setDateTime(new DateTime('2011-10-07', new DateTimeZone('UTC')));
330
331        $ev->add($dtStart);
332
333        $vcal->add($ev);
334
335        $it = new EventIterator($vcal,(string)$ev->uid);
336
337        // Grabbing the next 12 items
338        $max = 12;
339        $result = array();
340        foreach($it as $item) {
341
342            $result[] = $item;
343            $max--;
344
345            if (!$max) break;
346
347        }
348
349        $tz = new DateTimeZone('UTC');
350
351        $this->assertEquals(
352            array(
353                new DateTime('2011-10-07', $tz),
354                new DateTime('2011-10-11', $tz),
355                new DateTime('2011-10-19', $tz),
356                new DateTime('2011-10-21', $tz),
357                new DateTime('2011-10-25', $tz),
358                new DateTime('2011-11-02', $tz),
359                new DateTime('2011-11-04', $tz),
360                new DateTime('2011-11-08', $tz),
361                new DateTime('2011-11-16', $tz),
362                new DateTime('2011-11-18', $tz),
363                new DateTime('2011-11-22', $tz),
364                new DateTime('2011-11-30', $tz),
365            ),
366            $result
367        );
368
369    }
370
371    /**
372     * @depends testValues
373     */
374    function testWeekly() {
375
376        $vcal = new VCalendar();
377        $ev = $vcal->createComponent('VEVENT');
378
379        $ev->UID = 'bla';
380        $ev->RRULE = 'FREQ=WEEKLY;INTERVAL=2;COUNT=10';
381        $dtStart = $vcal->createProperty('DTSTART');
382        $dtStart->setDateTime(new DateTime('2011-10-07', new DateTimeZone('UTC')));
383
384        $ev->add($dtStart);
385
386        $vcal->add($ev);
387
388        $it = new EventIterator($vcal,(string)$ev->uid);
389
390        // Max is to prevent overflow
391        $max = 12;
392        $result = array();
393        foreach($it as $item) {
394
395            $result[] = $item;
396            $max--;
397
398            if (!$max) break;
399
400        }
401
402        $tz = new DateTimeZone('UTC');
403
404        $this->assertEquals(
405            array(
406                new DateTime('2011-10-07', $tz),
407                new DateTime('2011-10-21', $tz),
408                new DateTime('2011-11-04', $tz),
409                new DateTime('2011-11-18', $tz),
410                new DateTime('2011-12-02', $tz),
411                new DateTime('2011-12-16', $tz),
412                new DateTime('2011-12-30', $tz),
413                new DateTime('2012-01-13', $tz),
414                new DateTime('2012-01-27', $tz),
415                new DateTime('2012-02-10', $tz),
416            ),
417            $result
418        );
419
420    }
421
422    /**
423     * @depends testValues
424     */
425    function testWeeklyByDayByHour() {
426
427        $vcal = new VCalendar();
428        $ev = $vcal->createComponent('VEVENT');
429
430        $ev->UID = 'bla';
431        $ev->RRULE = 'FREQ=WEEKLY;INTERVAL=2;BYDAY=TU,WE,FR;WKST=MO;BYHOUR=8,9,10';
432        $dtStart = $vcal->createProperty('DTSTART');
433        $dtStart->setDateTime(new DateTime('2011-10-07 08:00:00', new DateTimeZone('UTC')));
434
435        $ev->add($dtStart);
436
437        $vcal->add($ev);
438
439        $it = new EventIterator($vcal,(string)$ev->uid);
440
441        // Grabbing the next 12 items
442        $max = 15;
443        $result = array();
444        foreach($it as $item) {
445
446            $result[] = $item;
447            $max--;
448
449            if (!$max) break;
450
451        }
452
453        $tz = new DateTimeZone('UTC');
454
455        $this->assertEquals(
456            array(
457                new DateTime('2011-10-07 08:00:00', $tz),
458                new DateTime('2011-10-07 09:00:00', $tz),
459                new DateTime('2011-10-07 10:00:00', $tz),
460                new DateTime('2011-10-18 08:00:00', $tz),
461                new DateTime('2011-10-18 09:00:00', $tz),
462                new DateTime('2011-10-18 10:00:00', $tz),
463                new DateTime('2011-10-19 08:00:00', $tz),
464                new DateTime('2011-10-19 09:00:00', $tz),
465                new DateTime('2011-10-19 10:00:00', $tz),
466                new DateTime('2011-10-21 08:00:00', $tz),
467                new DateTime('2011-10-21 09:00:00', $tz),
468                new DateTime('2011-10-21 10:00:00', $tz),
469                new DateTime('2011-11-01 08:00:00', $tz),
470                new DateTime('2011-11-01 09:00:00', $tz),
471                new DateTime('2011-11-01 10:00:00', $tz),
472            ),
473            $result
474        );
475
476    }
477
478    /**
479     * @depends testValues
480     */
481    function testWeeklyByDaySpecificHour() {
482
483        $vcal = new VCalendar();
484        $ev = $vcal->createComponent('VEVENT');
485
486        $ev->UID = 'bla';
487        $ev->RRULE = 'FREQ=WEEKLY;INTERVAL=2;BYDAY=TU,WE,FR;WKST=SU';
488        $dtStart = $vcal->createProperty('DTSTART');
489        $dtStart->setDateTime(new DateTime('2011-10-07 18:00:00', new DateTimeZone('UTC')));
490
491        $ev->add($dtStart);
492
493        $vcal->add($ev);
494
495        $it = new EventIterator($vcal,(string)$ev->uid);
496
497        // Grabbing the next 12 items
498        $max = 12;
499        $result = array();
500        foreach($it as $item) {
501
502            $result[] = $item;
503            $max--;
504
505            if (!$max) break;
506
507        }
508
509        $tz = new DateTimeZone('UTC');
510
511        $this->assertEquals(
512            array(
513                new DateTime('2011-10-07 18:00:00', $tz),
514                new DateTime('2011-10-18 18:00:00', $tz),
515                new DateTime('2011-10-19 18:00:00', $tz),
516                new DateTime('2011-10-21 18:00:00', $tz),
517                new DateTime('2011-11-01 18:00:00', $tz),
518                new DateTime('2011-11-02 18:00:00', $tz),
519                new DateTime('2011-11-04 18:00:00', $tz),
520                new DateTime('2011-11-15 18:00:00', $tz),
521                new DateTime('2011-11-16 18:00:00', $tz),
522                new DateTime('2011-11-18 18:00:00', $tz),
523                new DateTime('2011-11-29 18:00:00', $tz),
524                new DateTime('2011-11-30 18:00:00', $tz),
525            ),
526            $result
527        );
528
529    }
530
531    /**
532     * @depends testValues
533     */
534    function testWeeklyByDay() {
535
536        $vcal = new VCalendar();
537        $ev = $vcal->createComponent('VEVENT');
538
539        $ev->UID = 'bla';
540        $ev->RRULE = 'FREQ=WEEKLY;INTERVAL=2;BYDAY=TU,WE,FR;WKST=SU';
541        $dtStart = $vcal->createProperty('DTSTART');
542        $dtStart->setDateTime(new DateTime('2011-10-07', new DateTimeZone('UTC')));
543
544        $ev->add($dtStart);
545
546        $vcal->add($ev);
547
548        $it = new EventIterator($vcal,(string)$ev->uid);
549
550        // Grabbing the next 12 items
551        $max = 12;
552        $result = array();
553        foreach($it as $item) {
554
555            $result[] = $item;
556            $max--;
557
558            if (!$max) break;
559
560        }
561
562        $tz = new DateTimeZone('UTC');
563
564        $this->assertEquals(
565            array(
566                new DateTime('2011-10-07', $tz),
567                new DateTime('2011-10-18', $tz),
568                new DateTime('2011-10-19', $tz),
569                new DateTime('2011-10-21', $tz),
570                new DateTime('2011-11-01', $tz),
571                new DateTime('2011-11-02', $tz),
572                new DateTime('2011-11-04', $tz),
573                new DateTime('2011-11-15', $tz),
574                new DateTime('2011-11-16', $tz),
575                new DateTime('2011-11-18', $tz),
576                new DateTime('2011-11-29', $tz),
577                new DateTime('2011-11-30', $tz),
578            ),
579            $result
580        );
581
582    }
583
584    /**
585     * @depends testValues
586     */
587    function testMonthly() {
588
589        $vcal = new VCalendar();
590        $ev = $vcal->createComponent('VEVENT');
591
592        $ev->UID = 'bla';
593        $ev->RRULE = 'FREQ=MONTHLY;INTERVAL=3;COUNT=5';
594        $dtStart = $vcal->createProperty('DTSTART');
595        $dtStart->setDateTime(new DateTime('2011-12-05', new DateTimeZone('UTC')));
596
597        $ev->add($dtStart);
598
599        $vcal->add($ev);
600
601        $it = new EventIterator($vcal,(string)$ev->uid);
602
603        $max = 14;
604        $result = array();
605        foreach($it as $item) {
606
607            $result[] = $item;
608            $max--;
609
610            if (!$max) break;
611
612        }
613
614        $tz = new DateTimeZone('UTC');
615
616        $this->assertEquals(
617            array(
618                new DateTime('2011-12-05', $tz),
619                new DateTime('2012-03-05', $tz),
620                new DateTime('2012-06-05', $tz),
621                new DateTime('2012-09-05', $tz),
622                new DateTime('2012-12-05', $tz),
623            ),
624            $result
625        );
626
627
628    }
629
630    /**
631     * @depends testValues
632     */
633    function testMonthlyEndOfMonth() {
634
635        $vcal = new VCalendar();
636        $ev = $vcal->createComponent('VEVENT');
637
638        $ev->UID = 'bla';
639        $ev->RRULE = 'FREQ=MONTHLY;INTERVAL=2;COUNT=12';
640        $dtStart = $vcal->createProperty('DTSTART');
641        $dtStart->setDateTime(new DateTime('2011-12-31', new DateTimeZone('UTC')));
642
643        $ev->add($dtStart);
644
645        $vcal->add($ev);
646
647        $it = new EventIterator($vcal,(string)$ev->uid);
648
649        $max = 14;
650        $result = array();
651        foreach($it as $item) {
652
653            $result[] = $item;
654            $max--;
655
656            if (!$max) break;
657
658        }
659
660        $tz = new DateTimeZone('UTC');
661
662        $this->assertEquals(
663            array(
664                new DateTime('2011-12-31', $tz),
665                new DateTime('2012-08-31', $tz),
666                new DateTime('2012-10-31', $tz),
667                new DateTime('2012-12-31', $tz),
668                new DateTime('2013-08-31', $tz),
669                new DateTime('2013-10-31', $tz),
670                new DateTime('2013-12-31', $tz),
671                new DateTime('2014-08-31', $tz),
672                new DateTime('2014-10-31', $tz),
673                new DateTime('2014-12-31', $tz),
674                new DateTime('2015-08-31', $tz),
675                new DateTime('2015-10-31', $tz),
676            ),
677            $result
678        );
679
680
681    }
682
683    /**
684     * @depends testValues
685     */
686    function testMonthlyByMonthDay() {
687
688        $vcal = new VCalendar();
689        $ev = $vcal->createComponent('VEVENT');
690
691        $ev->UID = 'bla';
692        $ev->RRULE = 'FREQ=MONTHLY;INTERVAL=5;COUNT=9;BYMONTHDAY=1,31,-7';
693        $dtStart = $vcal->createProperty('DTSTART');
694        $dtStart->setDateTime(new DateTime('2011-01-01', new DateTimeZone('UTC')));
695
696        $ev->add($dtStart);
697
698        $vcal->add($ev);
699
700        $it = new EventIterator($vcal,(string)$ev->uid);
701
702        $max = 14;
703        $result = array();
704        foreach($it as $item) {
705
706            $result[] = $item;
707            $max--;
708
709            if (!$max) break;
710
711        }
712
713        $tz = new DateTimeZone('UTC');
714
715        $this->assertEquals(
716            array(
717                new DateTime('2011-01-01', $tz),
718                new DateTime('2011-01-25', $tz),
719                new DateTime('2011-01-31', $tz),
720                new DateTime('2011-06-01', $tz),
721                new DateTime('2011-06-24', $tz),
722                new DateTime('2011-11-01', $tz),
723                new DateTime('2011-11-24', $tz),
724                new DateTime('2012-04-01', $tz),
725                new DateTime('2012-04-24', $tz),
726            ),
727            $result
728        );
729
730    }
731
732    /**
733     * A pretty slow test. Had to be marked as 'medium' for phpunit to not die
734     * after 1 second. Would be good to optimize later.
735     *
736     * @depends testValues
737     * @medium
738     */
739    function testMonthlyByDay() {
740
741        $vcal = new VCalendar();
742        $ev = $vcal->createComponent('VEVENT');
743
744        $ev->UID = 'bla';
745        $ev->RRULE = 'FREQ=MONTHLY;INTERVAL=2;COUNT=16;BYDAY=MO,-2TU,+1WE,3TH';
746        $dtStart = $vcal->createProperty('DTSTART');
747        $dtStart->setDateTime(new DateTime('2011-01-03', new DateTimeZone('UTC')));
748
749        $ev->add($dtStart);
750
751        $vcal->add($ev);
752
753        $it = new EventIterator($vcal,(string)$ev->uid);
754
755        $max = 20;
756        $result = array();
757        foreach($it as $k=>$item) {
758
759            $result[] = $item;
760            $max--;
761
762            if (!$max) break;
763
764        }
765
766        $tz = new DateTimeZone('UTC');
767
768        $this->assertEquals(
769            array(
770                new DateTime('2011-01-03', $tz),
771                new DateTime('2011-01-05', $tz),
772                new DateTime('2011-01-10', $tz),
773                new DateTime('2011-01-17', $tz),
774                new DateTime('2011-01-18', $tz),
775                new DateTime('2011-01-20', $tz),
776                new DateTime('2011-01-24', $tz),
777                new DateTime('2011-01-31', $tz),
778                new DateTime('2011-03-02', $tz),
779                new DateTime('2011-03-07', $tz),
780                new DateTime('2011-03-14', $tz),
781                new DateTime('2011-03-17', $tz),
782                new DateTime('2011-03-21', $tz),
783                new DateTime('2011-03-22', $tz),
784                new DateTime('2011-03-28', $tz),
785                new DateTime('2011-05-02', $tz),
786            ),
787            $result
788        );
789
790    }
791
792    /**
793     * @depends testValues
794     */
795    function testMonthlyByDayByMonthDay() {
796
797        $vcal = new VCalendar();
798        $ev = $vcal->createComponent('VEVENT');
799
800        $ev->UID = 'bla';
801        $ev->RRULE = 'FREQ=MONTHLY;COUNT=10;BYDAY=MO;BYMONTHDAY=1';
802        $dtStart = $vcal->createProperty('DTSTART');
803        $dtStart->setDateTime(new DateTime('2011-08-01', new DateTimeZone('UTC')));
804
805        $ev->add($dtStart);
806
807        $vcal->add($ev);
808
809        $it = new EventIterator($vcal,(string)$ev->uid);
810
811        $max = 20;
812        $result = array();
813        foreach($it as $k=>$item) {
814
815            $result[] = $item;
816            $max--;
817
818            if (!$max) break;
819
820        }
821
822        $tz = new DateTimeZone('UTC');
823
824        $this->assertEquals(
825            array(
826                new DateTime('2011-08-01', $tz),
827                new DateTime('2012-10-01', $tz),
828                new DateTime('2013-04-01', $tz),
829                new DateTime('2013-07-01', $tz),
830                new DateTime('2014-09-01', $tz),
831                new DateTime('2014-12-01', $tz),
832                new DateTime('2015-06-01', $tz),
833                new DateTime('2016-02-01', $tz),
834                new DateTime('2016-08-01', $tz),
835                new DateTime('2017-05-01', $tz),
836            ),
837            $result
838        );
839
840    }
841
842    /**
843     * @depends testValues
844     */
845    function testMonthlyByDayBySetPos() {
846
847        $vcal = new VCalendar();
848        $ev = $vcal->createComponent('VEVENT');
849
850        $ev->UID = 'bla';
851        $ev->RRULE = 'FREQ=MONTHLY;COUNT=10;BYDAY=MO,TU,WE,TH,FR;BYSETPOS=1,-1';
852        $dtStart = $vcal->createProperty('DTSTART');
853        $dtStart->setDateTime(new DateTime('2011-01-03', new DateTimeZone('UTC')));
854
855        $ev->add($dtStart);
856
857        $vcal->add($ev);
858
859        $it = new EventIterator($vcal,(string)$ev->uid);
860
861        $max = 20;
862        $result = array();
863        foreach($it as $k=>$item) {
864
865            $result[] = $item;
866            $max--;
867
868            if (!$max) break;
869
870        }
871
872        $tz = new DateTimeZone('UTC');
873
874        $this->assertEquals(
875            array(
876                new DateTime('2011-01-03', $tz),
877                new DateTime('2011-01-31', $tz),
878                new DateTime('2011-02-01', $tz),
879                new DateTime('2011-02-28', $tz),
880                new DateTime('2011-03-01', $tz),
881                new DateTime('2011-03-31', $tz),
882                new DateTime('2011-04-01', $tz),
883                new DateTime('2011-04-29', $tz),
884                new DateTime('2011-05-02', $tz),
885                new DateTime('2011-05-31', $tz),
886            ),
887            $result
888        );
889
890    }
891
892    /**
893     * @depends testValues
894     */
895    function testYearly() {
896
897        $vcal = new VCalendar();
898        $ev = $vcal->createComponent('VEVENT');
899
900        $ev->UID = 'bla';
901        $ev->RRULE = 'FREQ=YEARLY;COUNT=10;INTERVAL=3';
902        $dtStart = $vcal->createProperty('DTSTART');
903        $dtStart->setDateTime(new DateTime('2011-01-01', new DateTimeZone('UTC')));
904
905        $ev->add($dtStart);
906
907        $vcal->add($ev);
908
909        $it = new EventIterator($vcal,(string)$ev->uid);
910
911        $max = 20;
912        $result = array();
913        foreach($it as $k=>$item) {
914
915            $result[] = $item;
916            $max--;
917
918            if (!$max) break;
919
920        }
921
922        $tz = new DateTimeZone('UTC');
923
924        $this->assertEquals(
925            array(
926                new DateTime('2011-01-01', $tz),
927                new DateTime('2014-01-01', $tz),
928                new DateTime('2017-01-01', $tz),
929                new DateTime('2020-01-01', $tz),
930                new DateTime('2023-01-01', $tz),
931                new DateTime('2026-01-01', $tz),
932                new DateTime('2029-01-01', $tz),
933                new DateTime('2032-01-01', $tz),
934                new DateTime('2035-01-01', $tz),
935                new DateTime('2038-01-01', $tz),
936            ),
937            $result
938        );
939
940    }
941
942    /**
943     * @depends testValues
944     */
945    function testYearlyLeapYear() {
946
947        $vcal = new VCalendar();
948        $ev = $vcal->createComponent('VEVENT');
949
950        $ev->UID = 'bla';
951        $ev->RRULE = 'FREQ=YEARLY;COUNT=3';
952        $dtStart = $vcal->createProperty('DTSTART');
953        $dtStart->setDateTime(new DateTime('2012-02-29', new DateTimeZone('UTC')));
954
955        $ev->add($dtStart);
956
957        $vcal->add($ev);
958
959        $it = new EventIterator($vcal,(string)$ev->uid);
960
961        $max = 20;
962        $result = array();
963        foreach($it as $k=>$item) {
964
965            $result[] = $item;
966            $max--;
967
968            if (!$max) break;
969
970        }
971
972        $tz = new DateTimeZone('UTC');
973
974        $this->assertEquals(
975            array(
976                new DateTime('2012-02-29', $tz),
977                new DateTime('2016-02-29', $tz),
978                new DateTime('2020-02-29', $tz),
979            ),
980            $result
981        );
982
983    }
984
985    /**
986     * @depends testValues
987     */
988    function testYearlyByMonth() {
989
990        $vcal = new VCalendar();
991        $ev = $vcal->createComponent('VEVENT');
992
993        $ev->UID = 'bla';
994        $ev->RRULE = 'FREQ=YEARLY;COUNT=8;INTERVAL=4;BYMONTH=4,10';
995        $dtStart = $vcal->createProperty('DTSTART');
996        $dtStart->setDateTime(new DateTime('2011-04-07', new DateTimeZone('UTC')));
997
998        $ev->add($dtStart);
999
1000        $vcal->add($ev);
1001
1002        $it = new EventIterator($vcal,(string)$ev->uid);
1003
1004        $max = 20;
1005        $result = array();
1006        foreach($it as $k=>$item) {
1007
1008            $result[] = $item;
1009            $max--;
1010
1011            if (!$max) break;
1012
1013        }
1014
1015        $tz = new DateTimeZone('UTC');
1016
1017        $this->assertEquals(
1018            array(
1019                new DateTime('2011-04-07', $tz),
1020                new DateTime('2011-10-07', $tz),
1021                new DateTime('2015-04-07', $tz),
1022                new DateTime('2015-10-07', $tz),
1023                new DateTime('2019-04-07', $tz),
1024                new DateTime('2019-10-07', $tz),
1025                new DateTime('2023-04-07', $tz),
1026                new DateTime('2023-10-07', $tz),
1027            ),
1028            $result
1029        );
1030
1031    }
1032
1033    /**
1034     * @depends testValues
1035     */
1036    function testYearlyByMonthByDay() {
1037
1038        $vcal = new VCalendar();
1039        $ev = $vcal->createComponent('VEVENT');
1040
1041        $ev->UID = 'bla';
1042        $ev->RRULE = 'FREQ=YEARLY;COUNT=8;INTERVAL=5;BYMONTH=4,10;BYDAY=1MO,-1SU';
1043        $dtStart = $vcal->createProperty('DTSTART');
1044        $dtStart->setDateTime(new DateTime('2011-04-04', new DateTimeZone('UTC')));
1045
1046        $ev->add($dtStart);
1047
1048        $vcal->add($ev);
1049
1050        $it = new EventIterator($vcal,(string)$ev->uid);
1051
1052        $max = 20;
1053        $result = array();
1054        foreach($it as $k=>$item) {
1055
1056            $result[] = $item;
1057            $max--;
1058
1059            if (!$max) break;
1060
1061        }
1062
1063        $tz = new DateTimeZone('UTC');
1064
1065        $this->assertEquals(
1066            array(
1067                new DateTime('2011-04-04', $tz),
1068                new DateTime('2011-04-24', $tz),
1069                new DateTime('2011-10-03', $tz),
1070                new DateTime('2011-10-30', $tz),
1071                new DateTime('2016-04-04', $tz),
1072                new DateTime('2016-04-24', $tz),
1073                new DateTime('2016-10-03', $tz),
1074                new DateTime('2016-10-30', $tz),
1075            ),
1076            $result
1077        );
1078
1079    }
1080
1081    /**
1082     * @depends testValues
1083     */
1084    function testFastForward() {
1085
1086        $vcal = new VCalendar();
1087        $ev = $vcal->createComponent('VEVENT');
1088
1089        $ev->UID = 'bla';
1090        $ev->RRULE = 'FREQ=YEARLY;COUNT=8;INTERVAL=5;BYMONTH=4,10;BYDAY=1MO,-1SU';
1091        $dtStart = $vcal->createProperty('DTSTART');
1092        $dtStart->setDateTime(new DateTime('2011-04-04', new DateTimeZone('UTC')));
1093
1094        $ev->add($dtStart);
1095
1096        $vcal->add($ev);
1097
1098        $it = new EventIterator($vcal,(string)$ev->uid);
1099
1100        // The idea is that we're fast-forwarding too far in the future, so
1101        // there will be no results left.
1102        $it->fastForward(new DateTime('2020-05-05', new DateTimeZone('UTC')));
1103
1104        $max = 20;
1105        $result = array();
1106        while($item = $it->current()) {
1107
1108            $result[] = $item;
1109            $max--;
1110
1111            if (!$max) break;
1112            $it->next();
1113
1114        }
1115
1116        $tz = new DateTimeZone('UTC');
1117        $this->assertEquals(array(), $result);
1118
1119    }
1120
1121    /**
1122     * @depends testValues
1123     */
1124    function testComplexExclusions() {
1125
1126        $vcal = new VCalendar();
1127        $ev = $vcal->createComponent('VEVENT');
1128
1129        $ev->UID = 'bla';
1130        $ev->RRULE = 'FREQ=YEARLY;COUNT=10';
1131        $dtStart = $vcal->createProperty('DTSTART');
1132
1133        $tz = new DateTimeZone('Canada/Eastern');
1134        $dtStart->setDateTime(new DateTime('2011-01-01 13:50:20', $tz));
1135
1136        $exDate1 = $vcal->createProperty('EXDATE');
1137        $exDate1->setDateTimes(array(new DateTime('2012-01-01 13:50:20', $tz), new DateTime('2014-01-01 13:50:20', $tz)));
1138        $exDate2 = $vcal->createProperty('EXDATE');
1139        $exDate2->setDateTimes(array(new DateTime('2016-01-01 13:50:20', $tz)));
1140
1141        $ev->add($dtStart);
1142        $ev->add($exDate1);
1143        $ev->add($exDate2);
1144
1145        $vcal->add($ev);
1146
1147        $it = new EventIterator($vcal,(string)$ev->uid);
1148
1149        $max = 20;
1150        $result = array();
1151        foreach($it as $k=>$item) {
1152
1153            $result[] = $item;
1154            $max--;
1155
1156            if (!$max) break;
1157
1158        }
1159
1160        $this->assertEquals(
1161            array(
1162                new DateTime('2011-01-01 13:50:20', $tz),
1163                new DateTime('2013-01-01 13:50:20', $tz),
1164                new DateTime('2015-01-01 13:50:20', $tz),
1165                new DateTime('2017-01-01 13:50:20', $tz),
1166                new DateTime('2018-01-01 13:50:20', $tz),
1167                new DateTime('2019-01-01 13:50:20', $tz),
1168                new DateTime('2020-01-01 13:50:20', $tz),
1169            ),
1170            $result
1171        );
1172
1173    }
1174
1175    /**
1176     * @depends testValues
1177     */
1178    function testOverridenEvent() {
1179
1180        $vcal = new VCalendar();
1181
1182        $ev1 = $vcal->createComponent('VEVENT');
1183        $ev1->UID = 'overridden';
1184        $ev1->RRULE = 'FREQ=DAILY;COUNT=10';
1185        $ev1->DTSTART = '20120107T120000Z';
1186        $ev1->SUMMARY = 'baseEvent';
1187
1188        $vcal->add($ev1);
1189
1190        // ev2 overrides an event, and puts it on 2pm instead.
1191        $ev2 = $vcal->createComponent('VEVENT');
1192        $ev2->UID = 'overridden';
1193        $ev2->{'RECURRENCE-ID'} = '20120110T120000Z';
1194        $ev2->DTSTART = '20120110T140000Z';
1195        $ev2->SUMMARY = 'Event 2';
1196
1197        $vcal->add($ev2);
1198
1199        // ev3 overrides an event, and puts it 2 days and 2 hours later
1200        $ev3 = $vcal->createComponent('VEVENT');
1201        $ev3->UID = 'overridden';
1202        $ev3->{'RECURRENCE-ID'} = '20120113T120000Z';
1203        $ev3->DTSTART = '20120115T140000Z';
1204        $ev3->SUMMARY = 'Event 3';
1205
1206        $vcal->add($ev3);
1207
1208        $it = new EventIterator($vcal,'overridden');
1209
1210        $dates = array();
1211        $summaries = array();
1212        while($it->valid()) {
1213
1214            $dates[] = $it->getDTStart();
1215            $summaries[] = (string)$it->getEventObject()->SUMMARY;
1216            $it->next();
1217
1218        }
1219
1220        $tz = new DateTimeZone('UTC');
1221        $this->assertEquals(array(
1222            new DateTime('2012-01-07 12:00:00',$tz),
1223            new DateTime('2012-01-08 12:00:00',$tz),
1224            new DateTime('2012-01-09 12:00:00',$tz),
1225            new DateTime('2012-01-10 14:00:00',$tz),
1226            new DateTime('2012-01-11 12:00:00',$tz),
1227            new DateTime('2012-01-12 12:00:00',$tz),
1228            new DateTime('2012-01-14 12:00:00',$tz),
1229            new DateTime('2012-01-15 12:00:00',$tz),
1230            new DateTime('2012-01-15 14:00:00',$tz),
1231            new DateTime('2012-01-16 12:00:00',$tz),
1232        ), $dates);
1233
1234        $this->assertEquals(array(
1235            'baseEvent',
1236            'baseEvent',
1237            'baseEvent',
1238            'Event 2',
1239            'baseEvent',
1240            'baseEvent',
1241            'baseEvent',
1242            'baseEvent',
1243            'Event 3',
1244            'baseEvent',
1245        ), $summaries);
1246
1247    }
1248
1249    /**
1250     * @depends testValues
1251     */
1252    function testOverridenEvent2() {
1253
1254        $vcal = new VCalendar();
1255
1256        $ev1 = $vcal->createComponent('VEVENT');
1257        $ev1->UID = 'overridden';
1258        $ev1->RRULE = 'FREQ=WEEKLY;COUNT=3';
1259        $ev1->DTSTART = '20120112T120000Z';
1260        $ev1->SUMMARY = 'baseEvent';
1261
1262        $vcal->add($ev1);
1263
1264        // ev2 overrides an event, and puts it 6 days earlier instead.
1265        $ev2 = $vcal->createComponent('VEVENT');
1266        $ev2->UID = 'overridden';
1267        $ev2->{'RECURRENCE-ID'} = '20120119T120000Z';
1268        $ev2->DTSTART = '20120113T120000Z';
1269        $ev2->SUMMARY = 'Override!';
1270
1271        $vcal->add($ev2);
1272
1273        $it = new EventIterator($vcal,'overridden');
1274
1275        $dates = array();
1276        $summaries = array();
1277        while($it->valid()) {
1278
1279            $dates[] = $it->getDTStart();
1280            $summaries[] = (string)$it->getEventObject()->SUMMARY;
1281            $it->next();
1282
1283        }
1284
1285        $tz = new DateTimeZone('UTC');
1286        $this->assertEquals(array(
1287            new DateTime('2012-01-12 12:00:00',$tz),
1288            new DateTime('2012-01-13 12:00:00',$tz),
1289            new DateTime('2012-01-26 12:00:00',$tz),
1290
1291        ), $dates);
1292
1293        $this->assertEquals(array(
1294            'baseEvent',
1295            'Override!',
1296            'baseEvent',
1297        ), $summaries);
1298
1299    }
1300
1301    /**
1302     * @depends testValues
1303     */
1304    function testOverridenEventNoValuesExpected() {
1305
1306        $vcal = new VCalendar();
1307        $ev1 = $vcal->createComponent('VEVENT');
1308
1309        $ev1->UID = 'overridden';
1310        $ev1->RRULE = 'FREQ=WEEKLY;COUNT=3';
1311        $ev1->DTSTART = '20120124T120000Z';
1312        $ev1->SUMMARY = 'baseEvent';
1313
1314        $vcal->add($ev1);
1315
1316        // ev2 overrides an event, and puts it 6 days earlier instead.
1317        $ev2 = $vcal->createComponent('VEVENT');
1318        $ev2->UID = 'overridden';
1319        $ev2->{'RECURRENCE-ID'} = '20120131T120000Z';
1320        $ev2->DTSTART = '20120125T120000Z';
1321        $ev2->SUMMARY = 'Override!';
1322
1323        $vcal->add($ev2);
1324
1325        $it = new EventIterator($vcal,'overridden');
1326
1327        $dates = array();
1328        $summaries = array();
1329
1330        // The reported problem was specifically related to the VCALENDAR
1331        // expansion. In this parcitular case, we had to forward to the 28th of
1332        // january.
1333        $it->fastForward(new DateTime('2012-01-28 23:00:00'));
1334
1335        // We stop the loop when it hits the 6th of februari. Normally this
1336        // iterator would hit 24, 25 (overriden from 31) and 7 feb but because
1337        // we 'filter' from the 28th till the 6th, we should get 0 results.
1338        while($it->valid() && $it->getDTSTart() < new DateTime('2012-02-06 23:00:00')) {
1339
1340            $dates[] = $it->getDTStart();
1341            $summaries[] = (string)$it->getEventObject()->SUMMARY;
1342            $it->next();
1343
1344        }
1345
1346        $this->assertEquals(array(), $dates);
1347        $this->assertEquals(array(), $summaries);
1348
1349    }
1350
1351    /**
1352     * @depends testValues
1353     */
1354    function testRDATE() {
1355
1356        $vcal = new VCalendar();
1357        $ev = $vcal->createComponent('VEVENT');
1358
1359        $ev->UID = 'bla';
1360        $ev->RDATE = array(
1361            new DateTime('2014-08-07', new DateTimeZone('UTC')),
1362            new DateTime('2014-08-08', new DateTimeZone('UTC')),
1363        );
1364        $dtStart = $vcal->createProperty('DTSTART');
1365        $dtStart->setDateTime(new DateTime('2011-10-07', new DateTimeZone('UTC')));
1366
1367        $ev->add($dtStart);
1368
1369        $vcal->add($ev);
1370
1371        $it = new EventIterator($vcal,$ev->uid);
1372
1373        // Max is to prevent overflow
1374        $max = 12;
1375        $result = array();
1376        foreach($it as $item) {
1377
1378            $result[] = $item;
1379            $max--;
1380
1381            if (!$max) break;
1382
1383        }
1384
1385        $tz = new DateTimeZone('UTC');
1386
1387        $this->assertEquals(
1388            array(
1389                new DateTime('2011-10-07', $tz),
1390                new DateTime('2014-08-07', $tz),
1391                new DateTime('2014-08-08', $tz),
1392            ),
1393            $result
1394        );
1395
1396    }
1397
1398    /**
1399     * @depends testValues
1400     * @expectedException \InvalidArgumentException
1401     */
1402    function testNoMasterBadUID() {
1403
1404        $vcal = new VCalendar();
1405        // ev2 overrides an event, and puts it on 2pm instead.
1406        $ev2 = $vcal->createComponent('VEVENT');
1407        $ev2->UID = 'overridden';
1408        $ev2->{'RECURRENCE-ID'} = '20120110T120000Z';
1409        $ev2->DTSTART = '20120110T140000Z';
1410        $ev2->SUMMARY = 'Event 2';
1411
1412        $vcal->add($ev2);
1413
1414        // ev3 overrides an event, and puts it 2 days and 2 hours later
1415        $ev3 = $vcal->createComponent('VEVENT');
1416        $ev3->UID = 'overridden';
1417        $ev3->{'RECURRENCE-ID'} = '20120113T120000Z';
1418        $ev3->DTSTART = '20120115T140000Z';
1419        $ev3->SUMMARY = 'Event 3';
1420
1421        $vcal->add($ev3);
1422
1423        $it = new EventIterator($vcal,'broken');
1424
1425    }
1426}
1427
1428