RRuleIteratorTest.php 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713
  1. <?php
  2. namespace Sabre\VObject\Recur;
  3. use DateTime;
  4. use DateTimeZone;
  5. class RRuleIteratorTest extends \PHPUnit_Framework_TestCase {
  6. function testHourly() {
  7. $this->parse(
  8. 'FREQ=HOURLY;INTERVAL=3;COUNT=12',
  9. '2011-10-07 12:00:00',
  10. array(
  11. '2011-10-07 12:00:00',
  12. '2011-10-07 15:00:00',
  13. '2011-10-07 18:00:00',
  14. '2011-10-07 21:00:00',
  15. '2011-10-08 00:00:00',
  16. '2011-10-08 03:00:00',
  17. '2011-10-08 06:00:00',
  18. '2011-10-08 09:00:00',
  19. '2011-10-08 12:00:00',
  20. '2011-10-08 15:00:00',
  21. '2011-10-08 18:00:00',
  22. '2011-10-08 21:00:00',
  23. )
  24. );
  25. }
  26. function testDaily() {
  27. $this->parse(
  28. 'FREQ=DAILY;INTERVAL=3;UNTIL=20111025T000000Z',
  29. '2011-10-07',
  30. array(
  31. '2011-10-07 00:00:00',
  32. '2011-10-10 00:00:00',
  33. '2011-10-13 00:00:00',
  34. '2011-10-16 00:00:00',
  35. '2011-10-19 00:00:00',
  36. '2011-10-22 00:00:00',
  37. '2011-10-25 00:00:00',
  38. )
  39. );
  40. }
  41. function testDailyByDayByHour() {
  42. $this->parse(
  43. 'FREQ=DAILY;BYDAY=SA,SU;BYHOUR=6,7',
  44. '2011-10-08 06:00:00',
  45. array(
  46. '2011-10-08 06:00:00',
  47. '2011-10-08 07:00:00',
  48. '2011-10-09 06:00:00',
  49. '2011-10-09 07:00:00',
  50. '2011-10-15 06:00:00',
  51. '2011-10-15 07:00:00',
  52. '2011-10-16 06:00:00',
  53. '2011-10-16 07:00:00',
  54. '2011-10-22 06:00:00',
  55. '2011-10-22 07:00:00',
  56. '2011-10-23 06:00:00',
  57. '2011-10-23 07:00:00',
  58. )
  59. );
  60. }
  61. function testDailyByHour() {
  62. $this->parse(
  63. 'FREQ=DAILY;INTERVAL=2;BYHOUR=10,11,12,13,14,15',
  64. '2012-10-11 12:00:00',
  65. array(
  66. '2012-10-11 12:00:00',
  67. '2012-10-11 13:00:00',
  68. '2012-10-11 14:00:00',
  69. '2012-10-11 15:00:00',
  70. '2012-10-13 10:00:00',
  71. '2012-10-13 11:00:00',
  72. '2012-10-13 12:00:00',
  73. '2012-10-13 13:00:00',
  74. '2012-10-13 14:00:00',
  75. '2012-10-13 15:00:00',
  76. '2012-10-15 10:00:00',
  77. '2012-10-15 11:00:00',
  78. )
  79. );
  80. }
  81. function testDailyByDay() {
  82. $this->parse(
  83. 'FREQ=DAILY;INTERVAL=2;BYDAY=TU,WE,FR',
  84. '2011-10-07 12:00:00',
  85. array(
  86. '2011-10-07 12:00:00',
  87. '2011-10-11 12:00:00',
  88. '2011-10-19 12:00:00',
  89. '2011-10-21 12:00:00',
  90. '2011-10-25 12:00:00',
  91. '2011-11-02 12:00:00',
  92. '2011-11-04 12:00:00',
  93. '2011-11-08 12:00:00',
  94. '2011-11-16 12:00:00',
  95. '2011-11-18 12:00:00',
  96. '2011-11-22 12:00:00',
  97. '2011-11-30 12:00:00',
  98. )
  99. );
  100. }
  101. function testDailyCount() {
  102. $this->parse(
  103. 'FREQ=DAILY;COUNT=5',
  104. '2014-08-01 18:03:00',
  105. array(
  106. '2014-08-01 18:03:00',
  107. '2014-08-02 18:03:00',
  108. '2014-08-03 18:03:00',
  109. '2014-08-04 18:03:00',
  110. '2014-08-05 18:03:00',
  111. )
  112. );
  113. }
  114. function testDailyByMonth() {
  115. $this->parse(
  116. 'FREQ=DAILY;BYMONTH=9,10;BYDAY=SU',
  117. '2007-10-04 16:00:00',
  118. array(
  119. "2013-09-29 16:00:00",
  120. "2013-10-06 16:00:00",
  121. "2013-10-13 16:00:00",
  122. "2013-10-20 16:00:00",
  123. "2013-10-27 16:00:00",
  124. "2014-09-07 16:00:00"
  125. ),
  126. '2013-09-28'
  127. );
  128. }
  129. function testWeekly() {
  130. $this->parse(
  131. 'FREQ=WEEKLY;INTERVAL=2;COUNT=10',
  132. '2011-10-07 00:00:00',
  133. array(
  134. '2011-10-07 00:00:00',
  135. '2011-10-21 00:00:00',
  136. '2011-11-04 00:00:00',
  137. '2011-11-18 00:00:00',
  138. '2011-12-02 00:00:00',
  139. '2011-12-16 00:00:00',
  140. '2011-12-30 00:00:00',
  141. '2012-01-13 00:00:00',
  142. '2012-01-27 00:00:00',
  143. '2012-02-10 00:00:00',
  144. )
  145. );
  146. }
  147. function testWeeklyByDay() {
  148. $this->parse(
  149. 'FREQ=WEEKLY;INTERVAL=1;COUNT=4;BYDAY=MO;WKST=SA',
  150. '2014-08-01 00:00:00',
  151. array(
  152. '2014-08-01 00:00:00',
  153. '2014-08-04 00:00:00',
  154. '2014-08-11 00:00:00',
  155. '2014-08-18 00:00:00',
  156. )
  157. );
  158. }
  159. function testWeeklyByDay2() {
  160. $this->parse(
  161. 'FREQ=WEEKLY;INTERVAL=2;BYDAY=TU,WE,FR;WKST=SU',
  162. '2011-10-07 00:00:00',
  163. array(
  164. '2011-10-07 00:00:00',
  165. '2011-10-18 00:00:00',
  166. '2011-10-19 00:00:00',
  167. '2011-10-21 00:00:00',
  168. '2011-11-01 00:00:00',
  169. '2011-11-02 00:00:00',
  170. '2011-11-04 00:00:00',
  171. '2011-11-15 00:00:00',
  172. '2011-11-16 00:00:00',
  173. '2011-11-18 00:00:00',
  174. '2011-11-29 00:00:00',
  175. '2011-11-30 00:00:00',
  176. )
  177. );
  178. }
  179. function testWeeklyByDayByHour() {
  180. $this->parse(
  181. 'FREQ=WEEKLY;INTERVAL=2;BYDAY=TU,WE,FR;WKST=MO;BYHOUR=8,9,10',
  182. '2011-10-07 08:00:00',
  183. array(
  184. '2011-10-07 08:00:00',
  185. '2011-10-07 09:00:00',
  186. '2011-10-07 10:00:00',
  187. '2011-10-18 08:00:00',
  188. '2011-10-18 09:00:00',
  189. '2011-10-18 10:00:00',
  190. '2011-10-19 08:00:00',
  191. '2011-10-19 09:00:00',
  192. '2011-10-19 10:00:00',
  193. '2011-10-21 08:00:00',
  194. '2011-10-21 09:00:00',
  195. '2011-10-21 10:00:00',
  196. '2011-11-01 08:00:00',
  197. '2011-11-01 09:00:00',
  198. '2011-11-01 10:00:00',
  199. )
  200. );
  201. }
  202. function testWeeklyByDaySpecificHour() {
  203. $this->parse(
  204. 'FREQ=WEEKLY;INTERVAL=2;BYDAY=TU,WE,FR;WKST=SU',
  205. '2011-10-07 18:00:00',
  206. array(
  207. '2011-10-07 18:00:00',
  208. '2011-10-18 18:00:00',
  209. '2011-10-19 18:00:00',
  210. '2011-10-21 18:00:00',
  211. '2011-11-01 18:00:00',
  212. '2011-11-02 18:00:00',
  213. '2011-11-04 18:00:00',
  214. '2011-11-15 18:00:00',
  215. '2011-11-16 18:00:00',
  216. '2011-11-18 18:00:00',
  217. '2011-11-29 18:00:00',
  218. '2011-11-30 18:00:00',
  219. )
  220. );
  221. }
  222. function testMonthly() {
  223. $this->parse(
  224. 'FREQ=MONTHLY;INTERVAL=3;COUNT=5',
  225. '2011-12-05 00:00:00',
  226. array(
  227. '2011-12-05 00:00:00',
  228. '2012-03-05 00:00:00',
  229. '2012-06-05 00:00:00',
  230. '2012-09-05 00:00:00',
  231. '2012-12-05 00:00:00',
  232. )
  233. );
  234. }
  235. function testMonlthyEndOfMonth() {
  236. $this->parse(
  237. 'FREQ=MONTHLY;INTERVAL=2;COUNT=12',
  238. '2011-12-31 00:00:00',
  239. array(
  240. '2011-12-31 00:00:00',
  241. '2012-08-31 00:00:00',
  242. '2012-10-31 00:00:00',
  243. '2012-12-31 00:00:00',
  244. '2013-08-31 00:00:00',
  245. '2013-10-31 00:00:00',
  246. '2013-12-31 00:00:00',
  247. '2014-08-31 00:00:00',
  248. '2014-10-31 00:00:00',
  249. '2014-12-31 00:00:00',
  250. '2015-08-31 00:00:00',
  251. '2015-10-31 00:00:00',
  252. )
  253. );
  254. }
  255. function testMonthlyByMonthDay() {
  256. $this->parse(
  257. 'FREQ=MONTHLY;INTERVAL=5;COUNT=9;BYMONTHDAY=1,31,-7',
  258. '2011-01-01 00:00:00',
  259. array(
  260. '2011-01-01 00:00:00',
  261. '2011-01-25 00:00:00',
  262. '2011-01-31 00:00:00',
  263. '2011-06-01 00:00:00',
  264. '2011-06-24 00:00:00',
  265. '2011-11-01 00:00:00',
  266. '2011-11-24 00:00:00',
  267. '2012-04-01 00:00:00',
  268. '2012-04-24 00:00:00',
  269. )
  270. );
  271. }
  272. function testMonthlyByDay() {
  273. $this->parse(
  274. 'FREQ=MONTHLY;INTERVAL=2;COUNT=16;BYDAY=MO,-2TU,+1WE,3TH',
  275. '2011-01-03 00:00:00',
  276. array(
  277. '2011-01-03 00:00:00',
  278. '2011-01-05 00:00:00',
  279. '2011-01-10 00:00:00',
  280. '2011-01-17 00:00:00',
  281. '2011-01-18 00:00:00',
  282. '2011-01-20 00:00:00',
  283. '2011-01-24 00:00:00',
  284. '2011-01-31 00:00:00',
  285. '2011-03-02 00:00:00',
  286. '2011-03-07 00:00:00',
  287. '2011-03-14 00:00:00',
  288. '2011-03-17 00:00:00',
  289. '2011-03-21 00:00:00',
  290. '2011-03-22 00:00:00',
  291. '2011-03-28 00:00:00',
  292. '2011-05-02 00:00:00',
  293. )
  294. );
  295. }
  296. function testMonthlyByDayByMonthDay() {
  297. $this->parse(
  298. 'FREQ=MONTHLY;COUNT=10;BYDAY=MO;BYMONTHDAY=1',
  299. '2011-08-01 00:00:00',
  300. array(
  301. '2011-08-01 00:00:00',
  302. '2012-10-01 00:00:00',
  303. '2013-04-01 00:00:00',
  304. '2013-07-01 00:00:00',
  305. '2014-09-01 00:00:00',
  306. '2014-12-01 00:00:00',
  307. '2015-06-01 00:00:00',
  308. '2016-02-01 00:00:00',
  309. '2016-08-01 00:00:00',
  310. '2017-05-01 00:00:00',
  311. )
  312. );
  313. }
  314. function testMonthlyByDayBySetPos() {
  315. $this->parse(
  316. 'FREQ=MONTHLY;COUNT=10;BYDAY=MO,TU,WE,TH,FR;BYSETPOS=1,-1',
  317. '2011-01-03 00:00:00',
  318. array(
  319. '2011-01-03 00:00:00',
  320. '2011-01-31 00:00:00',
  321. '2011-02-01 00:00:00',
  322. '2011-02-28 00:00:00',
  323. '2011-03-01 00:00:00',
  324. '2011-03-31 00:00:00',
  325. '2011-04-01 00:00:00',
  326. '2011-04-29 00:00:00',
  327. '2011-05-02 00:00:00',
  328. '2011-05-31 00:00:00',
  329. )
  330. );
  331. }
  332. function testYearly() {
  333. $this->parse(
  334. 'FREQ=YEARLY;COUNT=10;INTERVAL=3',
  335. '2011-01-01 00:00:00',
  336. array(
  337. '2011-01-01 00:00:00',
  338. '2014-01-01 00:00:00',
  339. '2017-01-01 00:00:00',
  340. '2020-01-01 00:00:00',
  341. '2023-01-01 00:00:00',
  342. '2026-01-01 00:00:00',
  343. '2029-01-01 00:00:00',
  344. '2032-01-01 00:00:00',
  345. '2035-01-01 00:00:00',
  346. '2038-01-01 00:00:00',
  347. )
  348. );
  349. }
  350. function testYearlyLeapYear() {
  351. $this->parse(
  352. 'FREQ=YEARLY;COUNT=3',
  353. '2012-02-29 00:00:00',
  354. array(
  355. '2012-02-29 00:00:00',
  356. '2016-02-29 00:00:00',
  357. '2020-02-29 00:00:00',
  358. )
  359. );
  360. }
  361. function testYearlyByMonth() {
  362. $this->parse(
  363. 'FREQ=YEARLY;COUNT=8;INTERVAL=4;BYMONTH=4,10',
  364. '2011-04-07 00:00:00',
  365. array(
  366. '2011-04-07 00:00:00',
  367. '2011-10-07 00:00:00',
  368. '2015-04-07 00:00:00',
  369. '2015-10-07 00:00:00',
  370. '2019-04-07 00:00:00',
  371. '2019-10-07 00:00:00',
  372. '2023-04-07 00:00:00',
  373. '2023-10-07 00:00:00',
  374. )
  375. );
  376. }
  377. function testYearlyByMonthByDay() {
  378. $this->parse(
  379. 'FREQ=YEARLY;COUNT=8;INTERVAL=5;BYMONTH=4,10;BYDAY=1MO,-1SU',
  380. '2011-04-04 00:00:00',
  381. array(
  382. '2011-04-04 00:00:00',
  383. '2011-04-24 00:00:00',
  384. '2011-10-03 00:00:00',
  385. '2011-10-30 00:00:00',
  386. '2016-04-04 00:00:00',
  387. '2016-04-24 00:00:00',
  388. '2016-10-03 00:00:00',
  389. '2016-10-30 00:00:00',
  390. )
  391. );
  392. }
  393. function testFastForward() {
  394. // The idea is that we're fast-forwarding too far in the future, so
  395. // there will be no results left.
  396. $this->parse(
  397. 'FREQ=YEARLY;COUNT=8;INTERVAL=5;BYMONTH=4,10;BYDAY=1MO,-1SU',
  398. '2011-04-04 00:00:00',
  399. array(),
  400. '2020-05-05 00:00:00'
  401. );
  402. }
  403. /**
  404. * The bug that was in the
  405. * system before would fail on the 5th tuesday of the month, if the 5th
  406. * tuesday did not exist.
  407. *
  408. * A pretty slow test. Had to be marked as 'medium' for phpunit to not die
  409. * after 1 second. Would be good to optimize later.
  410. *
  411. * @medium
  412. */
  413. function testFifthTuesdayProblem() {
  414. $this->parse(
  415. 'FREQ=MONTHLY;INTERVAL=1;UNTIL=20071030T035959Z;BYDAY=5TU',
  416. '2007-10-04 14:46:42',
  417. array(
  418. "2007-10-04 14:46:42",
  419. )
  420. );
  421. }
  422. /**
  423. * This bug came from a Fruux customer. This would result in a never-ending
  424. * request.
  425. */
  426. function testFastFowardTooFar() {
  427. $this->parse(
  428. 'FREQ=WEEKLY;BYDAY=MO;UNTIL=20090704T205959Z;INTERVAL=1',
  429. '2009-04-20 18:00:00',
  430. array(
  431. '2009-04-20 18:00:00',
  432. '2009-04-27 18:00:00',
  433. '2009-05-04 18:00:00',
  434. '2009-05-11 18:00:00',
  435. '2009-05-18 18:00:00',
  436. '2009-05-25 18:00:00',
  437. '2009-06-01 18:00:00',
  438. '2009-06-08 18:00:00',
  439. '2009-06-15 18:00:00',
  440. '2009-06-22 18:00:00',
  441. '2009-06-29 18:00:00',
  442. )
  443. );
  444. }
  445. /**
  446. * This also at one point caused an infinite loop. We're keeping the test.
  447. */
  448. function testYearlyByMonthLoop() {
  449. $this->parse(
  450. 'FREQ=YEARLY;INTERVAL=1;UNTIL=20120203T225959Z;BYMONTH=2;BYSETPOS=1;BYDAY=SU,MO,TU,WE,TH,FR,SA',
  451. '2012-01-01 15:45:00',
  452. array(
  453. '2012-02-01 15:45:00',
  454. ),
  455. '2012-01-29 23:00:00'
  456. );
  457. }
  458. /**
  459. * Something, somewhere produced an ics with an interval set to 0. Because
  460. * this means we increase the current day (or week, month) by 0, this also
  461. * results in an infinite loop.
  462. *
  463. * @expectedException InvalidArgumentException
  464. */
  465. function testZeroInterval() {
  466. $this->parse(
  467. 'FREQ=YEARLY;INTERVAL=0',
  468. '2012-08-24 14:57:00',
  469. array(),
  470. '2013-01-01 23:00:00'
  471. );
  472. }
  473. /**
  474. * @expectedException InvalidArgumentException
  475. */
  476. function testInvalidFreq() {
  477. $this->parse(
  478. 'FREQ=SMONTHLY;INTERVAL=3;UNTIL=20111025T000000Z',
  479. '2011-10-07',
  480. array()
  481. );
  482. }
  483. /**
  484. * @expectedException InvalidArgumentException
  485. */
  486. function testByDayBadOffset() {
  487. $this->parse(
  488. 'FREQ=WEEKLY;INTERVAL=1;COUNT=4;BYDAY=0MO;WKST=SA',
  489. '2014-08-01 00:00:00',
  490. array()
  491. );
  492. }
  493. function testUntilBeginHAsTimezone() {
  494. $this->parse(
  495. 'FREQ=WEEKLY;UNTIL=20131118T183000',
  496. '2013-09-23 18:30:00',
  497. array(
  498. '2013-09-23 18:30:00',
  499. '2013-09-30 18:30:00',
  500. '2013-10-07 18:30:00',
  501. '2013-10-14 18:30:00',
  502. '2013-10-21 18:30:00',
  503. '2013-10-28 18:30:00',
  504. '2013-11-04 18:30:00',
  505. '2013-11-11 18:30:00',
  506. '2013-11-18 18:30:00',
  507. ),
  508. null,
  509. 'America/New_York'
  510. );
  511. }
  512. function testUntilBeforeDtStart() {
  513. $this->parse(
  514. 'FREQ=DAILY;UNTIL=20140101T000000Z',
  515. '2014-08-02 00:15:00',
  516. array(
  517. '2014-08-02 00:15:00',
  518. )
  519. );
  520. }
  521. function testIgnoredStuff() {
  522. $this->parse(
  523. 'FREQ=DAILY;BYSECOND=1;BYMINUTE=1;BYYEARDAY=1;BYWEEKNO=1;COUNT=2',
  524. '2014-08-02 00:15:00',
  525. array(
  526. '2014-08-02 00:15:00',
  527. '2014-08-03 00:15:00',
  528. )
  529. );
  530. }
  531. function testMinusFifthThursday() {
  532. $this->parse(
  533. 'FREQ=MONTHLY;BYDAY=-4TH,-5TH;COUNT=4',
  534. '2015-01-01 00:15:00',
  535. array(
  536. '2015-01-01 00:15:00',
  537. '2015-01-08 00:15:00',
  538. '2015-02-05 00:15:00',
  539. '2015-03-05 00:15:00'
  540. )
  541. );
  542. }
  543. /**
  544. * @expectedException InvalidArgumentException
  545. */
  546. function testUnsupportedPart() {
  547. $this->parse(
  548. 'FREQ=DAILY;BYWODAN=1',
  549. '2014-08-02 00:15:00',
  550. array()
  551. );
  552. }
  553. function testIteratorFunctions() {
  554. $parser = new RRuleIterator('FREQ=DAILY', new DateTime('2014-08-02 00:00:13'));
  555. $parser->next();
  556. $this->assertEquals(
  557. new DateTime('2014-08-03 00:00:13'),
  558. $parser->current()
  559. );
  560. $this->assertEquals(
  561. 1,
  562. $parser->key()
  563. );
  564. $parser->rewind();
  565. $this->assertEquals(
  566. new DateTime('2014-08-02 00:00:13'),
  567. $parser->current()
  568. );
  569. $this->assertEquals(
  570. 0,
  571. $parser->key()
  572. );
  573. }
  574. function parse($rule, $start, $expected, $fastForward = null, $tz = 'UTC') {
  575. $dt = new DateTime($start, new DateTimeZone($tz));
  576. $parser = new RRuleIterator($rule, $dt);
  577. if ($fastForward) {
  578. $parser->fastForward(new DateTime($fastForward));
  579. }
  580. $result = array();
  581. while($parser->valid()) {
  582. $item = $parser->current();
  583. $result[] = $item->format('Y-m-d H:i:s');
  584. if ($parser->isInfinite() && count($result) >= count($expected)) {
  585. break;
  586. }
  587. $parser->next();
  588. }
  589. $this->assertEquals(
  590. $expected,
  591. $result
  592. );
  593. }
  594. }