@recaptime-dev's working patches + fork for Phorge, a community fork of Phabricator. (Upstream dev and stable branches are at upstream/main and upstream/stable respectively.)
hq.recaptime.dev/wiki/Phorge
phorge
phabricator
1<?php
2
3final class PhutilCalendarRecurrenceRuleTestCase extends PhutilTestCase {
4
5 public function testSimpleRecurrenceRules() {
6 $start = PhutilCalendarAbsoluteDateTime::newFromISO8601('20160101T120000Z');
7
8 $rrule = id(new PhutilCalendarRecurrenceRule())
9 ->setStartDateTime($start)
10 ->setFrequency(PhutilCalendarRecurrenceRule::FREQUENCY_DAILY);
11
12 $set = id(new PhutilCalendarRecurrenceSet())
13 ->addSource($rrule);
14
15 $result = $set->getEventsBetween(null, null, 3);
16
17 $expect = array(
18 PhutilCalendarAbsoluteDateTime::newFromISO8601('20160101T120000Z'),
19 PhutilCalendarAbsoluteDateTime::newFromISO8601('20160102T120000Z'),
20 PhutilCalendarAbsoluteDateTime::newFromISO8601('20160103T120000Z'),
21 );
22
23 $this->assertEqual(
24 mpull($expect, 'getISO8601'),
25 mpull($result, 'getISO8601'),
26 pht('Simple daily event.'));
27
28
29
30 $rrule = id(new PhutilCalendarRecurrenceRule())
31 ->setStartDateTime($start)
32 ->setFrequency(PhutilCalendarRecurrenceRule::FREQUENCY_HOURLY)
33 ->setByHour(array(12, 13));
34
35 $set = id(new PhutilCalendarRecurrenceSet())
36 ->addSource($rrule);
37
38 $result = $set->getEventsBetween(null, null, 5);
39
40 $expect = array(
41 PhutilCalendarAbsoluteDateTime::newFromISO8601('20160101T120000Z'),
42 PhutilCalendarAbsoluteDateTime::newFromISO8601('20160101T130000Z'),
43 PhutilCalendarAbsoluteDateTime::newFromISO8601('20160102T120000Z'),
44 PhutilCalendarAbsoluteDateTime::newFromISO8601('20160102T130000Z'),
45 PhutilCalendarAbsoluteDateTime::newFromISO8601('20160103T120000Z'),
46 );
47
48 $this->assertEqual(
49 mpull($expect, 'getISO8601'),
50 mpull($result, 'getISO8601'),
51 pht('Hourly event with BYHOUR.'));
52
53
54 $rrule = id(new PhutilCalendarRecurrenceRule())
55 ->setStartDateTime($start)
56 ->setFrequency(PhutilCalendarRecurrenceRule::FREQUENCY_YEARLY);
57
58 $set = id(new PhutilCalendarRecurrenceSet())
59 ->addSource($rrule);
60
61 $result = $set->getEventsBetween(null, null, 2);
62
63 $expect = array(
64 PhutilCalendarAbsoluteDateTime::newFromISO8601('20160101T120000Z'),
65 PhutilCalendarAbsoluteDateTime::newFromISO8601('20170101T120000Z'),
66 );
67
68 $this->assertEqual(
69 mpull($expect, 'getISO8601'),
70 mpull($result, 'getISO8601'),
71 pht('Yearly event.'));
72
73
74 // This is an efficiency test for bizarre rules: it defines a secondly
75 // event which only occurs one a year, and generates 3 instances of it.
76 // This implementation should be fast enough that this test doesn't take
77 // a significant amount of time.
78
79 $rrule = id(new PhutilCalendarRecurrenceRule())
80 ->setStartDateTime($start)
81 ->setFrequency(PhutilCalendarRecurrenceRule::FREQUENCY_SECONDLY)
82 ->setByMonth(array(1))
83 ->setByMonthDay(array(1))
84 ->setByHour(array(12))
85 ->setByMinute(array(0))
86 ->setBySecond(array(0));
87
88 $set = id(new PhutilCalendarRecurrenceSet())
89 ->addSource($rrule);
90
91 $result = $set->getEventsBetween(null, null, 3);
92
93 $expect = array(
94 PhutilCalendarAbsoluteDateTime::newFromISO8601('20160101T120000Z'),
95 PhutilCalendarAbsoluteDateTime::newFromISO8601('20170101T120000Z'),
96 PhutilCalendarAbsoluteDateTime::newFromISO8601('20180101T120000Z'),
97 );
98
99 $this->assertEqual(
100 mpull($expect, 'getISO8601'),
101 mpull($result, 'getISO8601'),
102 pht('Secondly event with many constraints.'));
103 }
104
105 public function testYearlyRecurrenceRules() {
106 $tests = array();
107 $expect = array();
108
109 $tests[] = array();
110 $expect[] = array(
111 '19970902',
112 '19980902',
113 '19990902',
114 );
115
116 $tests[] = array(
117 'INTERVAL' => 2,
118 );
119 $expect[] = array(
120 '19970902',
121 '19990902',
122 '20010902',
123 );
124
125 $tests[] = array(
126 'DTSTART' => '20000229',
127 );
128 $expect[] = array(
129 '20000229',
130 '20040229',
131 '20080229',
132 );
133
134 $tests[] = array(
135 'BYMONTH' => array(1, 3),
136 );
137 $expect[] = array(
138 '19980102',
139 '19980302',
140 '19990102',
141 );
142
143 $tests[] = array(
144 'BYMONTHDAY' => array(1, 3),
145 );
146 $expect[] = array(
147 '19970903',
148 '19971001',
149 '19971003',
150 );
151
152 $tests[] = array(
153 'BYMONTH' => array(1, 3),
154 'BYMONTHDAY' => array(5, 7),
155 );
156 $expect[] = array(
157 '19980105',
158 '19980107',
159 '19980305',
160 );
161
162 $tests[] = array(
163 'BYDAY' => array('TU', 'TH'),
164 );
165 $expect[] = array(
166 '19970902',
167 '19970904',
168 '19970909',
169 );
170
171 $tests[] = array(
172 'BYDAY' => array('SU'),
173 );
174 $expect[] = array(
175 '19970907',
176 '19970914',
177 '19970921',
178 );
179
180 $tests[] = array(
181 'BYMONTH' => array(1, 3),
182 'BYDAY' => array('TU', 'TH'),
183 );
184 $expect[] = array(
185 '19980101',
186 '19980106',
187 '19980108',
188 );
189
190 $tests[] = array(
191 'BYMONTHDAY' => array(1, 3),
192 'BYDAY' => array('TU', 'TH'),
193 );
194 $expect[] = array(
195 '19980101',
196 '19980203',
197 '19980303',
198 );
199
200 $tests[] = array(
201 'BYMONTHDAY' => array(1, 3),
202 'BYDAY' => array('TU', 'TH'),
203 'BYMONTH' => array(1, 3),
204 );
205 $expect[] = array(
206 '19980101',
207 '19980303',
208 '20010301',
209 );
210
211 $tests[] = array(
212 'BYDAY' => array('1TU', '-1TH'),
213 );
214 $expect[] = array(
215 '19971225',
216 '19980106',
217 '19981231',
218 );
219
220 // Same test as above, just making sure the optional "+" syntax works.
221 $tests[] = array(
222 'BYDAY' => array('+1TU', '-1TH'),
223 );
224 $expect[] = array(
225 '19971225',
226 '19980106',
227 '19981231',
228 );
229
230 $tests[] = array(
231 'BYDAY' => array('3TU', '-3TH'),
232 );
233 $expect[] = array(
234 '19971211',
235 '19980120',
236 '19981217',
237 );
238
239 $tests[] = array(
240 'BYMONTH' => array(1, 3),
241 'BYDAY' => array('1TU', '-1TH'),
242 );
243 $expect[] = array(
244 '19980106',
245 '19980129',
246 '19980303',
247 );
248
249 $tests[] = array(
250 'BYMONTH' => array(1, 3),
251 'BYDAY' => array('3TU', '-3TH'),
252 );
253 $expect[] = array(
254 '19980115',
255 '19980120',
256 '19980312',
257 );
258
259 $tests[] = array(
260 'BYYEARDAY' => array(1, 100, 200, 365),
261 'COUNT' => 4,
262 );
263 $expect[] = array(
264 '19971231',
265 '19980101',
266 '19980410',
267 '19980719',
268 );
269
270 $tests[] = array(
271 'BYYEARDAY' => array(-365, -266, -166, -1),
272 'COUNT' => 4,
273 );
274 $expect[] = array(
275 '19971231',
276 '19980101',
277 '19980410',
278 '19980719',
279 );
280
281 $tests[] = array(
282 'BYYEARDAY' => array(1, 100, 200, 365),
283 'BYMONTH' => array(4, 7),
284 'COUNT' => 4,
285 );
286 $expect[] = array(
287 '19980410',
288 '19980719',
289 '19990410',
290 '19990719',
291 );
292
293 $tests[] = array(
294 'BYYEARDAY' => array(-365, -266, -166, -1),
295 'BYMONTH' => array(4, 7),
296 'COUNT' => 4,
297 );
298 $expect[] = array(
299 '19980410',
300 '19980719',
301 '19990410',
302 '19990719',
303 );
304
305 $tests[] = array(
306 'BYWEEKNO' => array(20),
307 );
308 $expect[] = array(
309 '19980511',
310 '19980512',
311 '19980513',
312 );
313
314 $tests[] = array(
315 'BYWEEKNO' => array(1),
316 'BYDAY' => array('MO'),
317 );
318 $expect[] = array(
319 '19971229',
320 '19990104',
321 '20000103',
322 );
323
324 $tests[] = array(
325 'BYWEEKNO' => array(52),
326 'BYDAY' => array('SU'),
327 );
328 $expect[] = array(
329 '19971228',
330 '19981227',
331 '20000102',
332 );
333
334 $tests[] = array(
335 'BYWEEKNO' => array(-1),
336 'BYDAY' => array('SU'),
337 );
338 $expect[] = array(
339 '19971228',
340 '19990103',
341 '20000102',
342 );
343
344 $tests[] = array(
345 'BYWEEKNO' => array(53),
346 'BYDAY' => array('MO'),
347 );
348 $expect[] = array(
349 '19981228',
350 '20041227',
351 '20091228',
352 );
353
354 $tests[] = array(
355 'BYHOUR' => array(6, 18),
356 );
357 $expect[] = array(
358 '19970902T060000Z',
359 '19970902T180000Z',
360 '19980902T060000Z',
361 );
362
363 $tests[] = array(
364 'BYMINUTE' => array(15, 30),
365 );
366 $expect[] = array(
367 '19970902T001500Z',
368 '19970902T003000Z',
369 '19980902T001500Z',
370 );
371
372 $tests[] = array(
373 'BYSECOND' => array(10, 20),
374 );
375 $expect[] = array(
376 '19970902T000010Z',
377 '19970902T000020Z',
378 '19980902T000010Z',
379 );
380
381 $tests[] = array(
382 'BYHOUR' => array(6, 18),
383 'BYMINUTE' => array(15, 30),
384 );
385 $expect[] = array(
386 '19970902T061500Z',
387 '19970902T063000Z',
388 '19970902T181500Z',
389 );
390
391 $tests[] = array(
392 'BYHOUR' => array(6, 18),
393 'BYSECOND' => array(10, 20),
394 );
395 $expect[] = array(
396 '19970902T060010Z',
397 '19970902T060020Z',
398 '19970902T180010Z',
399 );
400
401 $tests[] = array(
402 'BYMINUTE' => array(15, 30),
403 'BYSECOND' => array(10, 20),
404 );
405 $expect[] = array(
406 '19970902T001510Z',
407 '19970902T001520Z',
408 '19970902T003010Z',
409 );
410
411 $tests[] = array(
412 'BYHOUR' => array(6, 18),
413 'BYMINUTE' => array(15, 30),
414 'BYSECOND' => array(10, 20),
415 );
416 $expect[] = array(
417 '19970902T061510Z',
418 '19970902T061520Z',
419 '19970902T063010Z',
420 );
421
422 $tests[] = array(
423 'BYMONTHDAY' => array(15),
424 'BYHOUR' => array(6, 18),
425 'BYSETPOS' => array(3, -3),
426 );
427 $expect[] = array(
428 '19971115T180000Z',
429 '19980215T060000Z',
430 '19981115T180000Z',
431 );
432
433 $this->assertRules(
434 array(
435 'FREQ' => 'YEARLY',
436 'COUNT' => 3,
437 'DTSTART' => '19970902',
438 ),
439 $tests,
440 $expect);
441 }
442
443 public function testMonthlyRecurrenceRules() {
444 $tests = array();
445 $expect = array();
446
447 $tests[] = array();
448 $expect[] = array(
449 '19970902',
450 '19971002',
451 '19971102',
452 );
453
454 $tests[] = array(
455 'INTERVAL' => 2,
456 );
457 $expect[] = array(
458 '19970902',
459 '19971102',
460 '19980102',
461 );
462
463 $tests[] = array(
464 'INTERVAL' => 18,
465 );
466 $expect[] = array(
467 '19970902',
468 '19990302',
469 '20000902',
470 );
471
472 $tests[] = array(
473 'BYMONTH' => array(1, 3),
474 );
475 $expect[] = array(
476 '19980102',
477 '19980302',
478 '19990102',
479 );
480
481 $tests[] = array(
482 'BYMONTHDAY' => array(1, 3),
483 );
484 $expect[] = array(
485 '19970903',
486 '19971001',
487 '19971003',
488 );
489
490 $tests[] = array(
491 'BYMONTHDAY' => array(5, 7),
492 'BYMONTH' => array(1, 3),
493 );
494 $expect[] = array(
495 '19980105',
496 '19980107',
497 '19980305',
498 );
499
500 $tests[] = array(
501 'BYDAY' => array('TU', 'TH'),
502 );
503 $expect[] = array(
504 '19970902',
505 '19970904',
506 '19970909',
507 );
508
509 $tests[] = array(
510 'BYDAY' => array('3MO'),
511 );
512 $expect[] = array(
513 '19970915',
514 '19971020',
515 '19971117',
516 );
517
518 $tests[] = array(
519 'BYDAY' => array('1TU', '-1TH'),
520 );
521 $expect[] = array(
522 '19970902',
523 '19970925',
524 '19971007',
525 );
526
527 $tests[] = array(
528 'BYDAY' => array('3TU', '-3TH'),
529 );
530 $expect[] = array(
531 '19970911',
532 '19970916',
533 '19971016',
534 );
535
536 $tests[] = array(
537 'BYDAY' => array('TU', 'TH'),
538 'BYMONTH' => array(1, 3),
539 );
540 $expect[] = array(
541 '19980101',
542 '19980106',
543 '19980108',
544 );
545
546 $tests[] = array(
547 'BYMONTH' => array(1, 3),
548 'BYDAY' => array('1TU', '-1TH'),
549 );
550 $expect[] = array(
551 '19980106',
552 '19980129',
553 '19980303',
554 );
555
556 $tests[] = array(
557 'BYMONTH' => array(1, 3),
558 'BYDAY' => array('3TU', '-3TH'),
559 );
560 $expect[] = array(
561 '19980115',
562 '19980120',
563 '19980312',
564 );
565
566 $tests[] = array(
567 'BYMONTHDAY' => array(1, 3),
568 'BYDAY' => array('TU', 'TH'),
569 );
570 $expect[] = array(
571 '19980101',
572 '19980203',
573 '19980303',
574 );
575
576 $tests[] = array(
577 'BYMONTH' => array(1, 3),
578 'BYMONTHDAY' => array(1, 3),
579 'BYDAY' => array('TU', 'TH'),
580 );
581 $expect[] = array(
582 '19980101',
583 '19980303',
584 '20010301',
585 );
586
587 $tests[] = array(
588 'BYDAY' => array('MO', 'TU', 'WE', 'TH', 'FR'),
589 'BYSETPOS' => array(-1),
590 );
591 $expect[] = array(
592 '19970930',
593 '19971031',
594 '19971128',
595 );
596
597 $tests[] = array(
598 'BYDAY' => array('1MO', '1TU', '1WE', '1TH', '1FR', '-1FR'),
599 'BYMONTHDAY' => array(1, -1, -2),
600 );
601 $expect[] = array(
602 '19971001',
603 '19971031',
604 '19971201',
605 );
606
607 $tests[] = array(
608 'BYDAY' => array('1MO', '1TU', '1WE', '1TH', 'FR'),
609 'BYMONTHDAY' => array(1, -1, -2),
610 );
611 $expect[] = array(
612 '19971001',
613 '19971031',
614 '19971201',
615 );
616
617 $tests[] = array(
618 'BYHOUR' => array(6, 18),
619 );
620 $expect[] = array(
621 '19970902T060000Z',
622 '19970902T180000Z',
623 '19971002T060000Z',
624 );
625
626 $tests[] = array(
627 'BYMINUTE' => array(6, 18),
628 );
629 $expect[] = array(
630 '19970902T000600Z',
631 '19970902T001800Z',
632 '19971002T000600Z',
633 );
634
635 $tests[] = array(
636 'BYSECOND' => array(6, 18),
637 );
638 $expect[] = array(
639 '19970902T000006Z',
640 '19970902T000018Z',
641 '19971002T000006Z',
642 );
643
644 $tests[] = array(
645 'BYMONTHDAY' => array(13, 17),
646 'BYHOUR' => array(6, 18),
647 'BYSETPOS' => array(3, -3),
648 );
649 $expect[] = array(
650 '19970913T180000Z',
651 '19970917T060000Z',
652 '19971013T180000Z',
653 );
654
655 $tests[] = array(
656 'BYMONTHDAY' => array(13, 17),
657 'BYHOUR' => array(6, 18),
658 'BYSETPOS' => array(3, 3, -3),
659 );
660 $expect[] = array(
661 '19970913T180000Z',
662 '19970917T060000Z',
663 '19971013T180000Z',
664 );
665
666 $tests[] = array(
667 'BYMONTHDAY' => array(13, 17),
668 'BYHOUR' => array(6, 18),
669 'BYSETPOS' => array(4, -1),
670 );
671 $expect[] = array(
672 '19970917T180000Z',
673 '19971017T180000Z',
674 '19971117T180000Z',
675 );
676
677 $this->assertRules(
678 array(
679 'FREQ' => 'MONTHLY',
680 'COUNT' => 3,
681 'DTSTART' => '19970902',
682 ),
683 $tests,
684 $expect);
685 }
686
687 public function testWeeklyRecurrenceRules() {
688 $tests = array();
689 $expect = array();
690
691 $tests[] = array();
692 $expect[] = array(
693 '19970902',
694 '19970909',
695 '19970916',
696 );
697
698 $tests[] = array(
699 'INTERVAL' => 2,
700 );
701 $expect[] = array(
702 '19970902',
703 '19970916',
704 '19970930',
705 );
706
707 $tests[] = array(
708 'INTERVAL' => 20,
709 );
710 $expect[] = array(
711 '19970902',
712 '19980120',
713 '19980609',
714 );
715
716 $tests[] = array(
717 'BYMONTH' => array(1, 3),
718 );
719 $expect[] = array(
720 '19980106',
721 '19980113',
722 '19980120',
723 );
724
725 $tests[] = array(
726 'BYDAY' => array('TU', 'TH'),
727 );
728 $expect[] = array(
729 '19970902',
730 '19970904',
731 '19970909',
732 );
733
734 $tests[] = array(
735 'BYMONTH' => array(1, 3),
736 'BYDAY' => array('TU', 'TH'),
737 );
738 $expect[] = array(
739 '19980101',
740 '19980106',
741 '19980108',
742 );
743
744 $tests[] = array(
745 'BYHOUR' => array(6, 18),
746 );
747 $expect[] = array(
748 '19970902T060000Z',
749 '19970902T180000Z',
750 '19970909T060000Z',
751 );
752
753 $tests[] = array(
754 'BYDAY' => array('TU', 'TH'),
755 'BYHOUR' => array(6, 18),
756 'BYSETPOS' => array(3, -3),
757 'DTSTART' => '19970902T090000Z',
758 );
759 $expect[] = array(
760 '19970902T180000Z',
761 '19970904T060000Z',
762 '19970909T180000Z',
763 );
764
765 $this->assertRules(
766 array(
767 'FREQ' => 'WEEKLY',
768 'COUNT' => 3,
769 'DTSTART' => '19970902',
770 ),
771 $tests,
772 $expect);
773 }
774
775 public function testDailyRecurrenceRules() {
776 $tests = array();
777 $expect = array();
778
779 $tests[] = array();
780 $expect[] = array(
781 '19970902',
782 '19970903',
783 '19970904',
784 );
785
786 $tests[] = array(
787 'INTERVAL' => 2,
788 );
789 $expect[] = array(
790 '19970902',
791 '19970904',
792 '19970906',
793 );
794
795 $tests[] = array(
796 'INTERVAL' => 92,
797 );
798 $expect[] = array(
799 '19970902',
800 '19971203',
801 '19980305',
802 );
803
804 $tests[] = array(
805 'BYMONTH' => array(1, 3),
806 );
807 $expect[] = array(
808 '19980101',
809 '19980102',
810 '19980103',
811 );
812
813 // This is testing that INTERVAL is respected in the presence of a BYMONTH
814 // filter which skips some months.
815 $tests[] = array(
816 'BYMONTH' => array(12),
817 'INTERVAL' => 17,
818 );
819 $expect[] = array(
820 '19971213',
821 '19971230',
822 '19981205',
823 );
824
825 $tests[] = array(
826 'BYMONTHDAY' => array(1, 3),
827 );
828 $expect[] = array(
829 '19970903',
830 '19971001',
831 '19971003',
832 );
833
834 $tests[] = array(
835 'BYMONTH' => array(1, 3),
836 'BYMONTHDAY' => array(5, 7),
837 );
838 $expect[] = array(
839 '19980105',
840 '19980107',
841 '19980305',
842 );
843
844 $tests[] = array(
845 'BYDAY' => array('TU', 'TH'),
846 );
847 $expect[] = array(
848 '19970902',
849 '19970904',
850 '19970909',
851 );
852
853 $tests[] = array(
854 'BYMONTH' => array(1, 3),
855 'BYDAY' => array('TU', 'TH'),
856 );
857 $expect[] = array(
858 '19980101',
859 '19980106',
860 '19980108',
861 );
862
863 $tests[] = array(
864 'BYMONTHDAY' => array(1, 3),
865 'BYDAY' => array('TU', 'TH'),
866 );
867 $expect[] = array(
868 '19980101',
869 '19980203',
870 '19980303',
871 );
872
873 $tests[] = array(
874 'BYMONTH' => array(1, 3),
875 'BYMONTHDAY' => array(1, 3),
876 'BYDAY' => array('TU', 'TH'),
877 );
878 $expect[] = array(
879 '19980101',
880 '19980303',
881 '20010301',
882 );
883
884 $tests[] = array(
885 'BYHOUR' => array(6, 18),
886 'BYMINUTE' => array(15, 45),
887 'BYSETPOS' => array(3, -3),
888 'DTSTART' => '19970902T090000Z',
889 );
890 $expect[] = array(
891 '19970902T181500Z',
892 '19970903T064500Z',
893 '19970903T181500Z',
894 );
895
896 $this->assertRules(
897 array(
898 'FREQ' => 'DAILY',
899 'COUNT' => 3,
900 'DTSTART' => '19970902',
901 ),
902 $tests,
903 $expect);
904 }
905
906 public function testHourlyRecurrenceRules() {
907 $tests = array();
908 $expect = array();
909
910 $tests[] = array();
911 $expect[] = array(
912 '19970902T090000Z',
913 '19970902T100000Z',
914 '19970902T110000Z',
915 );
916
917 $tests[] = array(
918 'INTERVAL' => 2,
919 );
920 $expect[] = array(
921 '19970902T090000Z',
922 '19970902T110000Z',
923 '19970902T130000Z',
924 );
925
926 $tests[] = array(
927 'INTERVAL' => 769,
928 );
929 $expect[] = array(
930 '19970902T090000Z',
931 '19971004T100000Z',
932 '19971105T110000Z',
933 );
934
935 $tests[] = array(
936 'BYMONTH' => array(1, 3),
937 );
938 $expect[] = array(
939 '19980101T000000Z',
940 '19980101T010000Z',
941 '19980101T020000Z',
942 );
943
944 $tests[] = array(
945 'BYMONTHDAY' => array(1, 3),
946 );
947 $expect[] = array(
948 '19970903T000000Z',
949 '19970903T010000Z',
950 '19970903T020000Z',
951 );
952
953 $tests[] = array(
954 'BYMONTH' => array(1, 3),
955 'BYMONTHDAY' => array(5, 7),
956 );
957 $expect[] = array(
958 '19980105T000000Z',
959 '19980105T010000Z',
960 '19980105T020000Z',
961 );
962
963 $tests[] = array(
964 'BYDAY' => array('TU', 'TH'),
965 );
966 $expect[] = array(
967 '19970902T090000Z',
968 '19970902T100000Z',
969 '19970902T110000Z',
970 );
971
972 $tests[] = array(
973 'BYMONTH' => array(1, 3),
974 'BYDAY' => array('TU', 'TH'),
975 );
976 $expect[] = array(
977 '19980101T000000Z',
978 '19980101T010000Z',
979 '19980101T020000Z',
980 );
981
982 $tests[] = array(
983 'BYMONTHDAY' => array(1, 3),
984 'BYDAY' => array('TU', 'TH'),
985 );
986 $expect[] = array(
987 '19980101T000000Z',
988 '19980101T010000Z',
989 '19980101T020000Z',
990 );
991
992 $tests[] = array(
993 'BYMONTHDAY' => array(1, 3),
994 'BYMONTH' => array(1, 3),
995 'BYDAY' => array('TU', 'TH'),
996 );
997 $expect[] = array(
998 '19980101T000000Z',
999 '19980101T010000Z',
1000 '19980101T020000Z',
1001 );
1002
1003 $tests[] = array(
1004 'COUNT' => 4,
1005 'BYYEARDAY' => array(1, 100, 200, 365),
1006 );
1007 $expect[] = array(
1008 '19971231T000000Z',
1009 '19971231T010000Z',
1010 '19971231T020000Z',
1011 '19971231T030000Z',
1012 );
1013
1014 $tests[] = array(
1015 'COUNT' => 4,
1016 'BYYEARDAY' => array(-365, -266, -166, -1),
1017 );
1018 $expect[] = array(
1019 '19971231T000000Z',
1020 '19971231T010000Z',
1021 '19971231T020000Z',
1022 '19971231T030000Z',
1023 );
1024
1025 $tests[] = array(
1026 'COUNT' => 4,
1027 'BYMONTH' => array(4, 7),
1028 'BYYEARDAY' => array(1, 100, 200, 365),
1029 );
1030 $expect[] = array(
1031 '19980410T000000Z',
1032 '19980410T010000Z',
1033 '19980410T020000Z',
1034 '19980410T030000Z',
1035 );
1036
1037 $tests[] = array(
1038 'COUNT' => 4,
1039 'BYMONTH' => array(4, 7),
1040 'BYYEARDAY' => array(-365, -266, -166, -1),
1041 );
1042 $expect[] = array(
1043 '19980410T000000Z',
1044 '19980410T010000Z',
1045 '19980410T020000Z',
1046 '19980410T030000Z',
1047 );
1048
1049 $tests[] = array(
1050 'BYHOUR' => array(6, 18),
1051 );
1052 $expect[] = array(
1053 '19970902T180000Z',
1054 '19970903T060000Z',
1055 '19970903T180000Z',
1056 );
1057
1058 $tests[] = array(
1059 'BYMINUTE' => array(15, 45),
1060 'BYSECOND' => array(15, 45),
1061 'BYSETPOS' => array(3, -3),
1062 );
1063 $expect[] = array(
1064 '19970902T091545Z',
1065 '19970902T094515Z',
1066 '19970902T101545Z',
1067 );
1068
1069 $this->assertRules(
1070 array(
1071 'FREQ' => 'HOURLY',
1072 'COUNT' => 3,
1073 'DTSTART' => '19970902T090000Z',
1074 ),
1075 $tests,
1076 $expect);
1077 }
1078
1079 public function testMinutelyRecurrenceRules() {
1080 $tests = array();
1081 $expect = array();
1082
1083 $tests[] = array(
1084 );
1085 $expect[] = array(
1086 '19970902T090000Z',
1087 '19970902T090100Z',
1088 '19970902T090200Z',
1089 );
1090
1091 $tests[] = array(
1092 'INTERVAL' => 2,
1093 );
1094 $expect[] = array(
1095 '19970902T090000Z',
1096 '19970902T090200Z',
1097 '19970902T090400Z',
1098 );
1099
1100 $tests[] = array(
1101 'BYHOUR' => array(6, 18),
1102 'BYMINUTE' => array(6, 18),
1103 'BYSECOND' => array(6, 18),
1104 );
1105 $expect[] = array(
1106 '19970902T180606Z',
1107 '19970902T180618Z',
1108 '19970902T181806Z',
1109 );
1110
1111 $tests[] = array(
1112 'BYSECOND' => array(15, 30, 45),
1113 'BYSETPOS' => array(3, -3),
1114 );
1115 $expect[] = array(
1116 '19970902T090015Z',
1117 '19970902T090045Z',
1118 '19970902T090115Z',
1119 );
1120
1121 $this->assertRules(
1122 array(
1123 'FREQ' => 'MINUTELY',
1124 'COUNT' => 3,
1125 'DTSTART' => '19970902T090000Z',
1126 ),
1127 $tests,
1128 $expect);
1129 }
1130
1131 public function testSecondlyRecurrenceRules() {
1132 $tests = array();
1133 $expect = array();
1134
1135 $tests[] = array();
1136 $expect[] = array(
1137 '19970902T090000Z',
1138 '19970902T090001Z',
1139 '19970902T090002Z',
1140 );
1141
1142 $tests[] = array(
1143 'INTERVAL' => 2,
1144 );
1145 $expect[] = array(
1146 '19970902T090000Z',
1147 '19970902T090002Z',
1148 '19970902T090004Z',
1149 );
1150
1151 $tests[] = array(
1152 'INTERVAL' => 90061,
1153 );
1154 $expect[] = array(
1155 '19970902T090000Z',
1156 '19970903T100101Z',
1157 '19970904T110202Z',
1158 );
1159
1160 $tests[] = array(
1161 'BYSECOND' => array(0),
1162 'BYMINUTE' => array(1),
1163 'DTSTART' => '20100322T120100Z',
1164 );
1165 $expect[] = array(
1166 '20100322T120100Z',
1167 '20100322T130100Z',
1168 '20100322T140100Z',
1169 );
1170
1171 $this->assertRules(
1172 array(
1173 'FREQ' => 'SECONDLY',
1174 'COUNT' => 3,
1175 'DTSTART' => '19970902T090000Z',
1176 ),
1177 $tests,
1178 $expect);
1179 }
1180
1181 public function testRFC5545RecurrenceRules() {
1182 // These tests are derived from the examples in RFC5545.
1183 $tests = array();
1184 $expect = array();
1185
1186 $tests[] = array(
1187 'FREQ' => 'DAILY',
1188 'COUNT' => 10,
1189 'DTSTART' => '19970902T090000Z',
1190 );
1191 $expect[] = array(
1192 '19970902T090000Z',
1193 '19970903T090000Z',
1194 '19970904T090000Z',
1195 '19970905T090000Z',
1196 '19970906T090000Z',
1197 '19970907T090000Z',
1198 '19970908T090000Z',
1199 '19970909T090000Z',
1200 '19970910T090000Z',
1201 '19970911T090000Z',
1202 );
1203
1204 $tests[] = array(
1205 'FREQ' => 'DAILY',
1206 'INTERVAL' => 2,
1207 'DTSTART' => '19970902T090000Z',
1208 'COUNT' => 5,
1209 );
1210 $expect[] = array(
1211 '19970902T090000Z',
1212 '19970904T090000Z',
1213 '19970906T090000Z',
1214 '19970908T090000Z',
1215 '19970910T090000Z',
1216 );
1217
1218 $tests[] = array(
1219 'FREQ' => 'YEARLY',
1220 'BYMONTH' => array(1),
1221 'BYDAY' => array('MO', 'TU', 'WE', 'TH', 'FR', 'SA', 'SU'),
1222 'DTSTART' => '19970902T090000Z',
1223 'COUNT' => 3,
1224 );
1225 $expect[] = array(
1226 '19980101T090000Z',
1227 '19980102T090000Z',
1228 '19980103T090000Z',
1229 );
1230
1231 $tests[] = array(
1232 'FREQ' => 'MONTHLY',
1233 'COUNT' => 3,
1234 'BYDAY' => array('1FR'),
1235 'DTSTART' => '19970902T090000Z',
1236 );
1237 $expect[] = array(
1238 '19970905T090000Z',
1239 '19971003T090000Z',
1240 '19971107T090000Z',
1241 );
1242
1243 $tests[] = array(
1244 'FREQ' => 'MONTHLY',
1245 'INTERVAL' => 2,
1246 'COUNT' => 5,
1247 'BYDAY' => array('1SU', '-1SU'),
1248 'DTSTART' => '19970902T090000Z',
1249 );
1250 $expect[] = array(
1251 '19970907T090000Z',
1252 '19970928T090000Z',
1253 '19971102T090000Z',
1254 '19971130T090000Z',
1255 '19980104T090000Z',
1256 );
1257
1258 $tests[] = array(
1259 'FREQ' => 'MONTHLY',
1260 'COUNT' => 6,
1261 'BYDAY' => array('-2MO'),
1262 'DTSTART' => '19970902T090000Z',
1263 );
1264 $expect[] = array(
1265 '19970922T090000Z',
1266 '19971020T090000Z',
1267 '19971117T090000Z',
1268 '19971222T090000Z',
1269 '19980119T090000Z',
1270 '19980216T090000Z',
1271 );
1272
1273 $tests[] = array(
1274 'FREQ' => 'MONTHLY',
1275 'COUNT' => 6,
1276 'BYMONTHDAY' => array(-3),
1277 'DTSTART' => '19970902T090000Z',
1278 );
1279 $expect[] = array(
1280 '19970928T090000Z',
1281 '19971029T090000Z',
1282 '19971128T090000Z',
1283 '19971229T090000Z',
1284 '19980129T090000Z',
1285 '19980226T090000Z',
1286 );
1287
1288 $tests[] = array(
1289 'FREQ' => 'MONTHLY',
1290 'COUNT' => 5,
1291 'BYMONTHDAY' => array(2, 15),
1292 'DTSTART' => '19970902T090000Z',
1293 );
1294 $expect[] = array(
1295 '19970902T090000Z',
1296 '19970915T090000Z',
1297 '19971002T090000Z',
1298 '19971015T090000Z',
1299 '19971102T090000Z',
1300 );
1301
1302 $tests[] = array(
1303 'FREQ' => 'MONTHLY',
1304 'COUNT' => 5,
1305 'BYMONTHDAY' => array(-1, 1),
1306 'DTSTART' => '19970902T090000Z',
1307 );
1308 $expect[] = array(
1309 '19970930T090000Z',
1310 '19971001T090000Z',
1311 '19971031T090000Z',
1312 '19971101T090000Z',
1313 '19971130T090000Z',
1314 );
1315
1316 $tests[] = array(
1317 'FREQ' => 'MONTHLY',
1318 'COUNT' => 7,
1319 'INTERVAL' => 18,
1320 'BYMONTHDAY' => array(10, 11, 12, 13, 14, 15),
1321 'DTSTART' => '19970902T090000Z',
1322 );
1323 $expect[] = array(
1324 '19970910T090000Z',
1325 '19970911T090000Z',
1326 '19970912T090000Z',
1327 '19970913T090000Z',
1328 '19970914T090000Z',
1329 '19970915T090000Z',
1330 '19990310T090000Z',
1331 );
1332
1333 $tests[] = array(
1334 'FREQ' => 'MONTHLY',
1335 'COUNT' => 6,
1336 'INTERVAL' => 2,
1337 'BYDAY' => array('TU'),
1338 'DTSTART' => '19970902T090000Z',
1339 );
1340 $expect[] = array(
1341 '19970902T090000Z',
1342 '19970909T090000Z',
1343 '19970916T090000Z',
1344 '19970923T090000Z',
1345 '19970930T090000Z',
1346 '19971104T090000Z',
1347 );
1348
1349 $tests[] = array(
1350 'FREQ' => 'YEARLY',
1351 'COUNT' => 10,
1352 'BYMONTH' => array(6, 7),
1353 'DTSTART' => '19970610T090000Z',
1354 );
1355 $expect[] = array(
1356 '19970610T090000Z',
1357 '19970710T090000Z',
1358 '19980610T090000Z',
1359 '19980710T090000Z',
1360 '19990610T090000Z',
1361 '19990710T090000Z',
1362 '20000610T090000Z',
1363 '20000710T090000Z',
1364 '20010610T090000Z',
1365 '20010710T090000Z',
1366 );
1367
1368 $tests[] = array(
1369 'FREQ' => 'YEARLY',
1370 'COUNT' => 4,
1371 'INTERVAL' => 3,
1372 'BYYEARDAY' => array(1, 100, 200),
1373 'DTSTART' => '19970101T090000Z',
1374 );
1375 $expect[] = array(
1376 '19970101T090000Z',
1377 '19970410T090000Z',
1378 '19970719T090000Z',
1379 '20000101T090000Z',
1380 );
1381
1382 $tests[] = array(
1383 'FREQ' => 'YEARLY',
1384 'COUNT' => 3,
1385 'BYDAY' => array('20MO'),
1386 'DTSTART' => '19970519T090000Z',
1387 );
1388 $expect[] = array(
1389 '19970519T090000Z',
1390 '19980518T090000Z',
1391 '19990517T090000Z',
1392 );
1393
1394 $tests[] = array(
1395 'FREQ' => 'YEARLY',
1396 'COUNT' => 3,
1397 'BYWEEKNO' => array(20),
1398 'BYDAY' => array('MO'),
1399 'DTSTART' => '19970512T090000Z',
1400 );
1401 $expect[] = array(
1402 '19970512T090000Z',
1403 '19980511T090000Z',
1404 '19990517T090000Z',
1405 );
1406
1407 $tests[] = array(
1408 'FREQ' => 'YEARLY',
1409 'BYDAY' => array('TH'),
1410 'BYMONTH' => array(3),
1411 'DTSTART' => '19970313T090000Z',
1412 'COUNT' => 5,
1413 );
1414 $expect[] = array(
1415 '19970313T090000Z',
1416 '19970320T090000Z',
1417 '19970327T090000Z',
1418 '19980305T090000Z',
1419 '19980312T090000Z',
1420 );
1421
1422 $tests[] = array(
1423 'FREQ' => 'YEARLY',
1424 'BYDAY' => array('TH'),
1425 'BYMONTH' => array(6, 7, 8),
1426 'DTSTART' => '19970101T090000Z',
1427 'COUNT' => 15,
1428 );
1429 $expect[] = array(
1430 '19970605T090000Z',
1431 '19970612T090000Z',
1432 '19970619T090000Z',
1433 '19970626T090000Z',
1434 '19970703T090000Z',
1435 '19970710T090000Z',
1436 '19970717T090000Z',
1437 '19970724T090000Z',
1438 '19970731T090000Z',
1439 '19970807T090000Z',
1440 '19970814T090000Z',
1441 '19970821T090000Z',
1442 '19970828T090000Z',
1443 '19980604T090000Z',
1444 '19980611T090000Z',
1445 );
1446
1447 $tests[] = array(
1448 'FREQ' => 'YEARLY',
1449 'BYDAY' => array('FR'),
1450 'BYMONTHDAY' => array(13),
1451 'COUNT' => 4,
1452 'DTSTART' => '19970902T090000Z',
1453 );
1454 $expect[] = array(
1455 '19980213T090000Z',
1456 '19980313T090000Z',
1457 '19981113T090000Z',
1458 '19990813T090000Z',
1459 );
1460
1461 $tests[] = array(
1462 'FREQ' => 'MONTHLY',
1463 'BYDAY' => array('SA'),
1464 'BYMONTHDAY' => array(7, 8, 9, 10, 11, 12, 13),
1465 'COUNT' => 10,
1466 'DTSTART' => '19970902T090000Z',
1467 );
1468 $expect[] = array(
1469 '19970913T090000Z',
1470 '19971011T090000Z',
1471 '19971108T090000Z',
1472 '19971213T090000Z',
1473 '19980110T090000Z',
1474 '19980207T090000Z',
1475 '19980307T090000Z',
1476 '19980411T090000Z',
1477 '19980509T090000Z',
1478 '19980613T090000Z',
1479 );
1480
1481 $tests[] = array(
1482 'FREQ' => 'YEARLY',
1483 'INTERVAL' => 4,
1484 'BYMONTH' => array(11),
1485 'BYDAY' => array('TU'),
1486 'BYMONTHDAY' => array(2, 3, 4, 5, 6, 7, 8),
1487 'COUNT' => 6,
1488 'DTSTART' => '19961105T090000Z',
1489 );
1490 $expect[] = array(
1491 '19961105T090000Z',
1492 '20001107T090000Z',
1493 '20041102T090000Z',
1494 '20081104T090000Z',
1495 '20121106T090000Z',
1496 '20161108T090000Z',
1497 );
1498
1499 $tests[] = array(
1500 'FREQ' => 'MONTHLY',
1501 'BYDAY' => array('TU', 'WE', 'TH'),
1502 'BYSETPOS' => array(3),
1503 'COUNT' => 3,
1504 'DTSTART' => '19970904T090000Z',
1505 );
1506 $expect[] = array(
1507 '19970904T090000Z',
1508 '19971007T090000Z',
1509 '19971106T090000Z',
1510 );
1511
1512 $tests[] = array(
1513 'FREQ' => 'MONTHLY',
1514 'BYDAY' => array('MO', 'TU', 'WE', 'TH', 'FR'),
1515 'BYSETPOS' => array(-2),
1516 'COUNT' => 3,
1517 'DTSTART' => '19970929T090000Z',
1518 );
1519 $expect[] = array(
1520 '19970929T090000Z',
1521 '19971030T090000Z',
1522 '19971127T090000Z',
1523 );
1524
1525 $tests[] = array(
1526 'FREQ' => 'HOURLY',
1527 'INTERVAL' => 3,
1528 'DTSTART' => '19970929T090000Z',
1529 'COUNT' => 3,
1530 );
1531 $expect[] = array(
1532 '19970929T090000Z',
1533 '19970929T120000Z',
1534 '19970929T150000Z',
1535 );
1536
1537 $tests[] = array(
1538 'FREQ' => 'MINUTELY',
1539 'INTERVAL' => 15,
1540 'COUNT' => 6,
1541 'DTSTART' => '19970902T090000Z',
1542 );
1543 $expect[] = array(
1544 '19970902T090000Z',
1545 '19970902T091500Z',
1546 '19970902T093000Z',
1547 '19970902T094500Z',
1548 '19970902T100000Z',
1549 '19970902T101500Z',
1550 );
1551
1552 $tests[] = array(
1553 'FREQ' => 'MINUTELY',
1554 'INTERVAL' => 90,
1555 'COUNT' => 4,
1556 'DTSTART' => '19970902T090000Z',
1557 );
1558 $expect[] = array(
1559 '19970902T090000Z',
1560 '19970902T103000Z',
1561 '19970902T120000Z',
1562 '19970902T133000Z',
1563 );
1564
1565 $tests[] = array(
1566 'FREQ' => 'WEEKLY',
1567 'COUNT' => 10,
1568 'DTSTART' => '19970902T090000Z',
1569 );
1570 $expect[] = array(
1571 '19970902T090000Z',
1572 '19970909T090000Z',
1573 '19970916T090000Z',
1574 '19970923T090000Z',
1575 '19970930T090000Z',
1576 '19971007T090000Z',
1577 '19971014T090000Z',
1578 '19971021T090000Z',
1579 '19971028T090000Z',
1580 '19971104T090000Z',
1581 );
1582
1583 $tests[] = array(
1584 'FREQ' => 'WEEKLY',
1585 'INTERVAL' => 2,
1586 'COUNT' => 6,
1587 'DTSTART' => '19970902T090000Z',
1588 );
1589 $expect[] = array(
1590 '19970902T090000Z',
1591 '19970916T090000Z',
1592 '19970930T090000Z',
1593 '19971014T090000Z',
1594 '19971028T090000Z',
1595 '19971111T090000Z',
1596 );
1597
1598 $tests[] = array(
1599 'FREQ' => 'WEEKLY',
1600 'COUNT' => 10,
1601 'WKST' => 'SU',
1602 'BYDAY' => array('TU', 'TH'),
1603 'DTSTART' => '19970902T090000Z',
1604 );
1605 $expect[] = array(
1606 '19970902T090000Z',
1607 '19970904T090000Z',
1608 '19970909T090000Z',
1609 '19970911T090000Z',
1610 '19970916T090000Z',
1611 '19970918T090000Z',
1612 '19970923T090000Z',
1613 '19970925T090000Z',
1614 '19970930T090000Z',
1615 '19971002T090000Z',
1616 );
1617
1618 $tests[] = array(
1619 'FREQ' => 'WEEKLY',
1620 'INTERVAL' => 2,
1621 'COUNT' => 8,
1622 'WKST' => 'SU',
1623 'BYDAY' => array('TU', 'TH'),
1624 'DTSTART' => '19970902T090000Z',
1625 );
1626 $expect[] = array(
1627 '19970902T090000Z',
1628 '19970904T090000Z',
1629 '19970916T090000Z',
1630 '19970918T090000Z',
1631 '19970930T090000Z',
1632 '19971002T090000Z',
1633 '19971014T090000Z',
1634 '19971016T090000Z',
1635 );
1636
1637 $tests[] = array(
1638 'FREQ' => 'WEEKLY',
1639 'INTERVAL' => 2,
1640 'COUNT' => 4,
1641 'BYDAY' => array('TU', 'SU'),
1642 'WKST' => 'MO',
1643 'DTSTART' => '19970805T090000Z',
1644 );
1645 $expect[] = array(
1646 '19970805T090000Z',
1647 '19970810T090000Z',
1648 '19970819T090000Z',
1649 '19970824T090000Z',
1650 );
1651
1652 $tests[] = array(
1653 'FREQ' => 'WEEKLY',
1654 'INTERVAL' => 2,
1655 'COUNT' => 4,
1656 'BYDAY' => array('TU', 'SU'),
1657 'WKST' => 'SU',
1658 'DTSTART' => '19970805T090000Z',
1659 );
1660 $expect[] = array(
1661 '19970805T090000Z',
1662 '19970817T090000Z',
1663 '19970819T090000Z',
1664 '19970831T090000Z',
1665 );
1666
1667
1668 $this->assertRules(array(), $tests, $expect);
1669 }
1670
1671
1672 private function assertRules(array $defaults, array $tests, array $expect) {
1673 foreach ($tests as $key => $test) {
1674 $options = $test + $defaults;
1675
1676 $start = PhutilCalendarAbsoluteDateTime::newFromISO8601(
1677 $options['DTSTART']);
1678
1679 $rrule = id(new PhutilCalendarRecurrenceRule())
1680 ->setStartDateTime($start)
1681 ->setFrequency($options['FREQ']);
1682
1683 $interval = idx($options, 'INTERVAL');
1684 if ($interval) {
1685 $rrule->setInterval($interval);
1686 }
1687
1688 $by_day = idx($options, 'BYDAY');
1689 if ($by_day) {
1690 $rrule->setByDay($by_day);
1691 }
1692
1693 $by_month = idx($options, 'BYMONTH');
1694 if ($by_month) {
1695 $rrule->setByMonth($by_month);
1696 }
1697
1698 $by_monthday = idx($options, 'BYMONTHDAY');
1699 if ($by_monthday) {
1700 $rrule->setByMonthDay($by_monthday);
1701 }
1702
1703 $by_yearday = idx($options, 'BYYEARDAY');
1704 if ($by_yearday) {
1705 $rrule->setByYearDay($by_yearday);
1706 }
1707
1708 $by_weekno = idx($options, 'BYWEEKNO');
1709 if ($by_weekno) {
1710 $rrule->setByWeekNumber($by_weekno);
1711 }
1712
1713 $by_hour = idx($options, 'BYHOUR');
1714 if ($by_hour) {
1715 $rrule->setByHour($by_hour);
1716 }
1717
1718 $by_minute = idx($options, 'BYMINUTE');
1719 if ($by_minute) {
1720 $rrule->setByMinute($by_minute);
1721 }
1722
1723 $by_second = idx($options, 'BYSECOND');
1724 if ($by_second) {
1725 $rrule->setBySecond($by_second);
1726 }
1727
1728 $by_setpos = idx($options, 'BYSETPOS');
1729 if ($by_setpos) {
1730 $rrule->setBySetPosition($by_setpos);
1731 }
1732
1733 $week_start = idx($options, 'WKST');
1734 if ($week_start) {
1735 $rrule->setWeekStart($week_start);
1736 }
1737
1738 $set = id(new PhutilCalendarRecurrenceSet())
1739 ->addSource($rrule);
1740
1741 $result = $set->getEventsBetween(null, null, $options['COUNT']);
1742
1743 $this->assertEqual(
1744 $expect[$key],
1745 mpull($result, 'getISO8601'));
1746 }
1747 }
1748
1749
1750}