diff --git a/lib/Recur/RRuleIterator.php b/lib/Recur/RRuleIterator.php index 9b5465a7c..d0551ddc0 100644 --- a/lib/Recur/RRuleIterator.php +++ b/lib/Recur/RRuleIterator.php @@ -709,8 +709,8 @@ protected function nextMonthly($amount = 1): void $this->currentDate = $this->currentDate->setDate( (int) $this->currentDate->format('Y'), (int) $this->currentDate->format('n'), - (int) $occurrence - )->modify($this->startTime()); + $occurrence[0], + )->setTime($occurrence[1], $occurrence[2], $occurrence[3]); } /** @@ -905,8 +905,8 @@ protected function nextYearly($amount = 1): void $this->currentDate = $this->currentDate->setDate( (int) $currentYear, (int) $currentMonth, - (int) $occurrence - )->modify($this->startTime()); + $occurrence[0], + )->setTime($occurrence[1], $occurrence[2], $occurrence[3]); return; } else { diff --git a/tests/VObject/Recur/RRuleIteratorTest.php b/tests/VObject/Recur/RRuleIteratorTest.php index 43445f0ea..7ace7ae4f 100644 --- a/tests/VObject/Recur/RRuleIteratorTest.php +++ b/tests/VObject/Recur/RRuleIteratorTest.php @@ -30,122 +30,6 @@ public function testHourly(): void ); } - /** - * @dataProvider dst2HourlyTransitionProvider - */ - public function test2HourlyOnDstTransition(string $start, array $expected): void - { - $this->parse( - 'FREQ=HOURLY;INTERVAL=2;COUNT=5', - $start, - $expected, - null, - 'Europe/Zurich', - ); - } - - public function dst2HourlyTransitionProvider(): iterable - { - yield 'On transition start' => [ - 'Start' => '2023-03-26 00:00:00', - 'Expected' => [ - '2023-03-26 00:00:00', - '2023-03-26 03:00:00', - '2023-03-26 04:00:00', - '2023-03-26 06:00:00', - '2023-03-26 08:00:00', - ], - ]; - yield 'During transition' => [ - 'Start' => '2023-03-26 00:15:00', - 'Expected' => [ - '2023-03-26 00:15:00', - '2023-03-26 03:15:00', - '2023-03-26 04:15:00', - '2023-03-26 06:15:00', - '2023-03-26 08:15:00', - ], - ]; - yield 'On transition end' => [ - 'Start' => '2023-03-26 01:00:00', - 'Expected' => [ - '2023-03-26 01:00:00', - '2023-03-26 03:00:00', - '2023-03-26 05:00:00', - '2023-03-26 07:00:00', - '2023-03-26 09:00:00', - ], - ]; - yield 'After transition end' => [ - 'Start' => '2023-03-26 01:15:00', - 'Expected' => [ - '2023-03-26 01:15:00', - '2023-03-26 03:15:00', - '2023-03-26 05:15:00', - '2023-03-26 07:15:00', - '2023-03-26 09:15:00', - ], - ]; - } - - /** - * @dataProvider dst6HourlyTransitionProvider - */ - public function testHourlyOnDstTransition(string $start, array $expected): void - { - $this->parse( - 'FREQ=HOURLY;INTERVAL=6;COUNT=5', - $start, - $expected, - null, - 'Europe/Zurich', - ); - } - - public function dst6HourlyTransitionProvider(): iterable - { - yield 'On transition start' => [ - 'Start' => '2023-03-25 20:00:00', - 'Expected' => [ - '2023-03-25 20:00:00', - '2023-03-26 03:00:00', - '2023-03-26 08:00:00', - '2023-03-26 14:00:00', - '2023-03-26 20:00:00', - ], - ]; - yield 'During transition' => [ - 'Start' => '2023-03-25 20:15:00', - 'Expected' => [ - '2023-03-25 20:15:00', - '2023-03-26 03:15:00', - '2023-03-26 08:15:00', - '2023-03-26 14:15:00', - '2023-03-26 20:15:00', - ], - ]; - yield 'On transition end' => [ - 'Start' => '2023-03-25 21:00:00', - 'Expected' => [ - '2023-03-25 21:00:00', - '2023-03-26 03:00:00', - '2023-03-26 09:00:00', - '2023-03-26 15:00:00', - '2023-03-26 21:00:00', - ], - ]; - yield 'After transition end' => [ - 'Start' => '2023-03-25 21:15:00', - 'Expected' => [ - '2023-03-25 21:15:00', - '2023-03-26 03:15:00', - '2023-03-26 09:15:00', - '2023-03-26 15:15:00', - '2023-03-26 21:15:00', - ], - ]; - } - public function testDaily(): void { $this->parse( @@ -284,64 +168,6 @@ public function testDailyBySetPosLoop(): void ); } - /** - * @dataProvider dstDailyTransitionProvider - */ - public function testDailyOnDstTransition(string $start, array $expected): void - { - $this->parse( - 'FREQ=DAILY;INTERVAL=1;COUNT=5', - $start, - $expected, - null, - 'Europe/Zurich', - ); - } - - public function dstDailyTransitionProvider(): iterable - { - yield 'On transition start' => [ - 'Start' => '2023-03-24 02:00:00', - 'Expected' => [ - '2023-03-24 02:00:00', - '2023-03-25 02:00:00', - '2023-03-26 03:00:00', - '2023-03-27 02:00:00', - '2023-03-28 02:00:00', - ], - ]; - yield 'During transition' => [ - 'Start' => '2023-03-24 02:15:00', - 'Expected' => [ - '2023-03-24 02:15:00', - '2023-03-25 02:15:00', - '2023-03-26 03:15:00', - '2023-03-27 02:15:00', - '2023-03-28 02:15:00', - ], - ]; - yield 'On transition end' => [ - 'Start' => '2023-03-24 03:00:00', - 'Expected' => [ - '2023-03-24 03:00:00', - '2023-03-25 03:00:00', - '2023-03-26 03:00:00', - '2023-03-27 03:00:00', - '2023-03-28 03:00:00', - ], - ]; - yield 'After transition end' => [ - 'Start' => '2023-03-24 03:15:00', - 'Expected' => [ - '2023-03-24 03:15:00', - '2023-03-25 03:15:00', - '2023-03-26 03:15:00', - '2023-03-27 03:15:00', - '2023-03-28 03:15:00', - ], - ]; - } - public function testWeekly(): void { $this->parse( @@ -450,110 +276,6 @@ public function testWeeklyByDaySpecificHour(): void ); } - public function testWeeklyByDaySpecificHourOnDstTransition(): void - { - $this->parse( - 'FREQ=WEEKLY;INTERVAL=2;BYDAY=SA,SU', - '2023-03-11 02:30:00', - [ - '2023-03-11 02:30:00', - '2023-03-12 02:30:00', - '2023-03-25 02:30:00', - '2023-03-26 03:30:00', - '2023-04-08 02:30:00', - '2023-04-09 02:30:00', - ], - null, - 'Europe/Zurich', - ); - } - - public function testWeeklyByDayByHourOnDstTransition(): void - { - $this->parse( - 'FREQ=WEEKLY;INTERVAL=2;BYDAY=SA,SU;WKST=MO;BYHOUR=2,14', - '2023-03-11 02:00:00', - [ - '2023-03-11 02:00:00', - '2023-03-11 14:00:00', - '2023-03-12 02:00:00', - '2023-03-12 14:00:00', - '2023-03-25 02:00:00', - '2023-03-25 14:00:00', - // 02:00:00 does not exist on 2023-03-26 because of summer-time start. - // The current implementation logic does not schedule a recurrence on - // the morning of 2023-03-26. But maybe it should schedule one at 03:00:00. - // The RFC is silent about the required behavior in this case. - // '2023-03-26 03:00:00', - '2023-03-26 14:00:00', - '2023-04-08 02:00:00', - '2023-04-08 14:00:00', - '2023-04-09 02:00:00', - '2023-04-09 14:00:00', - ], - null, - 'Europe/Zurich', - ); - } - - /** - * @dataProvider dstWeeklyTransitionProvider - */ - public function testWeeklyOnDstTransition(string $start, array $expected): void - { - $this->parse( - 'FREQ=WEEKLY;INTERVAL=1;COUNT=5', - $start, - $expected, - null, - 'Europe/Zurich', - ); - } - - public function dstWeeklyTransitionProvider(): iterable - { - yield 'On transition start' => [ - 'Start' => '2023-03-12 02:00:00', - 'Expected' => [ - '2023-03-12 02:00:00', - '2023-03-19 02:00:00', - '2023-03-26 03:00:00', - '2023-04-02 02:00:00', - '2023-04-09 02:00:00', - ], - ]; - yield 'During transition' => [ - 'Start' => '2023-03-12 02:15:00', - 'Expected' => [ - '2023-03-12 02:15:00', - '2023-03-19 02:15:00', - '2023-03-26 03:15:00', - '2023-04-02 02:15:00', - '2023-04-09 02:15:00', - ], - ]; - yield 'On transition end' => [ - 'Start' => '2023-03-12 03:00:00', - 'Expected' => [ - '2023-03-12 03:00:00', - '2023-03-19 03:00:00', - '2023-03-26 03:00:00', - '2023-04-02 03:00:00', - '2023-04-09 03:00:00', - ], - ]; - yield 'After transition end' => [ - 'Start' => '2023-03-12 03:15:00', - 'Expected' => [ - '2023-03-12 03:15:00', - '2023-03-19 03:15:00', - '2023-03-26 03:15:00', - '2023-04-02 03:15:00', - '2023-04-09 03:15:00', - ], - ]; - } - public function testMonthly(): void { $this->parse( @@ -647,26 +369,6 @@ public function invalidFreqByCombinationProviders(): iterable ]; } - public function testMonthlyByMonthDayDstTransition(): void - { - $this->parse( - 'FREQ=MONTHLY;INTERVAL=1;COUNT=8;BYMONTHDAY=1,26', - '2023-01-01 02:15:00', - [ - '2023-01-01 02:15:00', - '2023-01-26 02:15:00', - '2023-02-01 02:15:00', - '2023-02-26 02:15:00', - '2023-03-01 02:15:00', - '2023-03-26 03:15:00', - '2023-04-01 02:15:00', - '2023-04-26 02:15:00', - ], - null, - 'Europe/Zurich', - ); - } - public function testMonthlyByDay(): void { $this->parse( @@ -711,31 +413,6 @@ public function testMonthlyByDayUntil(): void ); } - public function testMonthlyByDayOnDstTransition(): void - { - $this->parse( - 'FREQ=MONTHLY;INTERVAL=2;COUNT=13;BYDAY=SU', - '2023-01-01 02:30:00', - [ - '2023-01-01 02:30:00', - '2023-01-08 02:30:00', - '2023-01-15 02:30:00', - '2023-01-22 02:30:00', - '2023-01-29 02:30:00', - '2023-03-05 02:30:00', - '2023-03-12 02:30:00', - '2023-03-19 02:30:00', - '2023-03-26 03:30:00', - '2023-05-07 02:30:00', - '2023-05-14 02:30:00', - '2023-05-21 02:30:00', - '2023-05-28 02:30:00', - ], - null, - 'Europe/Zurich', - ); - } - public function testMonthlyByDayUntilWithImpossibleNextOccurrence(): void { $this->parse( @@ -790,74 +467,6 @@ public function testMonthlyByDayBySetPos(): void ); } - /** - * @dataProvider dstMonthlyTransitionProvider - */ - public function testMonthlyOnDstTransition(string $start, array $expected): void - { - $this->parse( - 'FREQ=MONTHLY;INTERVAL=1;COUNT=5', - $start, - $expected, - null, - 'Europe/Zurich', - ); - } - - public function dstMonthlyTransitionProvider(): iterable - { - yield 'On transition start' => [ - 'Start' => '2023-01-26 02:00:00', - 'Expected' => [ - '2023-01-26 02:00:00', - '2023-02-26 02:00:00', - '2023-03-26 03:00:00', - '2023-04-26 02:00:00', - '2023-05-26 02:00:00', - ], - ]; - yield 'During transition' => [ - 'Start' => '2023-01-26 02:15:00', - 'Expected' => [ - '2023-01-26 02:15:00', - '2023-02-26 02:15:00', - '2023-03-26 03:15:00', - '2023-04-26 02:15:00', - '2023-05-26 02:15:00', - ], - ]; - yield 'On transition end' => [ - 'Start' => '2023-01-26 03:00:00', - 'Expected' => [ - '2023-01-26 03:00:00', - '2023-02-26 03:00:00', - '2023-03-26 03:00:00', - '2023-04-26 03:00:00', - '2023-05-26 03:00:00', - ], - ]; - yield 'After transition end' => [ - 'Start' => '2023-01-26 03:15:00', - 'Expected' => [ - '2023-01-26 03:15:00', - '2023-02-26 03:15:00', - '2023-03-26 03:15:00', - '2023-04-26 03:15:00', - '2023-05-26 03:15:00', - ], - ]; - yield 'During transition on 31st day of month' => [ - 'Start' => '2024-01-31 02:15:00', - 'Expected' => [ - '2024-01-31 02:15:00', - '2024-03-31 03:15:00', - '2024-05-31 02:15:00', - '2024-07-31 02:15:00', - '2024-08-31 02:15:00', - ], - ]; - } - public function testYearly(): void { $this->parse( @@ -912,26 +521,6 @@ public function testYearlyByMonth(): void ); } - public function testYearlyByMonthOnDstTransition(): void - { - $this->parse( - 'FREQ=YEARLY;COUNT=8;INTERVAL=2;BYMONTH=3,9', - '2019-03-26 02:30:00', - [ - '2019-03-26 02:30:00', - '2019-09-26 02:30:00', - '2021-03-26 02:30:00', - '2021-09-26 02:30:00', - '2023-03-26 03:30:00', - '2023-09-26 02:30:00', - '2025-03-26 02:30:00', - '2025-09-26 02:30:00', - ], - null, - 'Europe/Zurich', - ); - } - public function testYearlyByMonthInvalidValue1(): void { $this->expectException(InvalidDataException::class); @@ -1009,31 +598,6 @@ public function testYearlyNewYearsEve() ); } - public function testYearlyByMonthByDayOnDstTransition(): void - { - $this->parse( - 'FREQ=YEARLY;COUNT=13;INTERVAL=2;BYMONTH=3;BYDAY=SU', - '2021-03-07 02:30:00', - [ - '2021-03-07 02:30:00', - '2021-03-14 02:30:00', - '2021-03-21 02:30:00', - '2021-03-28 03:30:00', - '2023-03-05 02:30:00', - '2023-03-12 02:30:00', - '2023-03-19 02:30:00', - '2023-03-26 03:30:00', - '2025-03-02 02:30:00', - '2025-03-09 02:30:00', - '2025-03-16 02:30:00', - '2025-03-23 02:30:00', - '2025-03-30 03:30:00', - ], - null, - 'Europe/Zurich', - ); - } - public function testYearlyNewYearsDay(): void { $this->parse( @@ -1273,64 +837,6 @@ public function testYearlyByDayByWeekNo(): void ); } - /** - * @dataProvider dstYearlyTransitionProvider - */ - public function testYearlyOnDstTransition(string $start, array $expected): void - { - $this->parse( - 'FREQ=YEARLY;INTERVAL=1;COUNT=5', - $start, - $expected, - null, - 'Europe/Zurich', - ); - } - - public function dstYearlyTransitionProvider(): iterable - { - yield 'On transition start' => [ - 'Start' => '2021-03-26 02:00:00', - 'Expected' => [ - '2021-03-26 02:00:00', - '2022-03-26 02:00:00', - '2023-03-26 03:00:00', - '2024-03-26 02:00:00', - '2025-03-26 02:00:00', - ], - ]; - yield 'During transition' => [ - 'Start' => '2021-03-26 02:15:00', - 'Expected' => [ - '2021-03-26 02:15:00', - '2022-03-26 02:15:00', - '2023-03-26 03:15:00', - '2024-03-26 02:15:00', - '2025-03-26 02:15:00', - ], - ]; - yield 'On transition end' => [ - 'Start' => '2021-03-26 03:00:00', - 'Expected' => [ - '2021-03-26 03:00:00', - '2022-03-26 03:00:00', - '2023-03-26 03:00:00', - '2024-03-26 03:00:00', - '2025-03-26 03:00:00', - ], - ]; - yield 'After transition end' => [ - 'Start' => '2021-03-26 03:15:00', - 'Expected' => [ - '2021-03-26 03:15:00', - '2022-03-26 03:15:00', - '2023-03-26 03:15:00', - '2024-03-26 03:15:00', - '2025-03-26 03:15:00', - ], - ]; - } - public function testFastForward(): void { // The idea is that we're fast-forwarding too far in the future, so @@ -1557,52 +1063,6 @@ public function testYearlyBySetPosLoop(): void ); } - /** - * This caused an incorrect date to be returned by the rule iterator when - * start date was not on the rrule list. - * - * @dataProvider yearlyStartDateNotOnRRuleListProvider - */ - public function testYearlyStartDateNotOnRRuleList(string $rule, string $start, array $expected): void - { - $this->parse($rule, $start, $expected); - } - - public function yearlyStartDateNotOnRRuleListProvider(): array - { - return [ - [ - 'FREQ=YEARLY;BYMONTH=6;BYDAY=-1FR;UNTIL=20250901T000000Z', - '2023-09-01 12:00:00', - [ - '2023-09-01 12:00:00', - '2024-06-28 12:00:00', - '2025-06-27 12:00:00', - ], - ], - [ - 'FREQ=YEARLY;BYMONTH=6;BYDAY=-1FR;UNTIL=20250901T000000Z', - '2023-06-01 12:00:00', - [ - '2023-06-01 12:00:00', - '2023-06-30 12:00:00', - '2024-06-28 12:00:00', - '2025-06-27 12:00:00', - ], - ], - [ - 'FREQ=YEARLY;BYMONTH=6;BYDAY=-1FR;UNTIL=20250901T000000Z', - '2023-05-01 12:00:00', - [ - '2023-05-01 12:00:00', - '2023-06-30 12:00:00', - '2024-06-28 12:00:00', - '2025-06-27 12:00:00', - ], - ], - ]; - } - /** * Something, somewhere produced an ics with an interval set to 0. Because * this means we increase the current day (or week, month) by 0, this also