From dbb49e8d48f2f10c6aee54dd0a6028156a86afdd Mon Sep 17 00:00:00 2001 From: Jun Pataleta Date: Fri, 17 Mar 2017 13:27:20 +0800 Subject: [PATCH 1/2] MDL-57793 calendar: Add recursion to satisfy COUNT rule * Add recursion to the creation of recurring calendar events to satisfy the COUNT rule, if necessary. --- calendar/classes/rrule_manager.php | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/calendar/classes/rrule_manager.php b/calendar/classes/rrule_manager.php index 78cee59681d..ea8e1846375 100644 --- a/calendar/classes/rrule_manager.php +++ b/calendar/classes/rrule_manager.php @@ -721,6 +721,20 @@ class rrule_manager { unset($cloneevent->id); \calendar_event::create($cloneevent, false); } + + // If COUNT rule is defined and the number of the generated event times is less than the the COUNT rule, + // repeat the processing until the COUNT rule is satisfied. + if ($count !== false && $count > 0) { + // Set count to the remaining counts. + $this->count = $count; + // Clone the original event, but set the timestart to the last generated event time. + $tmpevent = clone($event); + $tmpevent->timestart = end($eventtimes); + // Generate the additional event times. + $additionaleventtimes = $this->generate_recurring_event_times($tmpevent); + // Create the additional events. + $this->create_recurring_events($event, $additionaleventtimes); + } } /** From 6d5661a9b3c72144e9766696c3b5ecce47bb0b60 Mon Sep 17 00:00:00 2001 From: Jun Pataleta Date: Fri, 24 Mar 2017 12:29:49 +0800 Subject: [PATCH 2/2] MDL-57793 calendar: Additional edge-case tests * Added leap year and 31th day of the month tests. --- calendar/tests/rrule_manager_test.php | 53 +++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) diff --git a/calendar/tests/rrule_manager_test.php b/calendar/tests/rrule_manager_test.php index 2b0f315a953..9178329b81f 100644 --- a/calendar/tests/rrule_manager_test.php +++ b/calendar/tests/rrule_manager_test.php @@ -2691,6 +2691,59 @@ class core_calendar_rrule_manager_testcase extends advanced_testcase { } } + /* + * Other edge case tests. + */ + + /** + * Tests for MONTHLY RRULE with BYMONTHDAY set to 31. + * Should not include February, April, June, September and November. + */ + public function test_monthly_bymonthday_31() { + global $DB; + + $rrule = 'FREQ=MONTHLY;BYMONTHDAY=31;COUNT=20'; + $mang = new rrule_manager($rrule); + $mang->parse_rrule(); + $mang->create_events($this->event); + + $records = $DB->get_records('event', ['repeatid' => $this->event->id], 'timestart ASC', 'id, repeatid, timestart'); + $this->assertCount(20, $records); + + $non31months = ['February', 'April', 'June', 'September', 'November']; + + foreach ($records as $record) { + $month = date('F', $record->timestart); + $this->assertNotContains($month, $non31months); + } + } + + /** + * Tests for the last day in February. (Leap year test) + */ + public function test_yearly_on_the_last_day_of_february() { + global $DB; + + $rrule = 'FREQ=YEARLY;BYMONTH=2;BYMONTHDAY=-1;COUNT=30'; + $mang = new rrule_manager($rrule); + $mang->parse_rrule(); + $mang->create_events($this->event); + + $records = $DB->get_records('event', ['repeatid' => $this->event->id], 'timestart ASC', 'id, repeatid, timestart'); + $this->assertCount(30, $records); + + foreach ($records as $record) { + $date = new DateTime(date('Y-m-d H:i:s', $record->timestart)); + $year = $date->format('Y'); + $day = $date->format('d'); + if ($year % 4 == 0) { + $this->assertEquals(29, $day); + } else { + $this->assertEquals(28, $day); + } + } + } + /** * Change the event's timestart (DTSTART) based on the test's needs. *