diff --git a/src/parser/calendar/data/PhutilCalendarRecurrenceRule.php b/src/parser/calendar/data/PhutilCalendarRecurrenceRule.php --- a/src/parser/calendar/data/PhutilCalendarRecurrenceRule.php +++ b/src/parser/calendar/data/PhutilCalendarRecurrenceRule.php @@ -42,8 +42,6 @@ private $stateMonth; private $stateYear; - private $initialMonth; - private $initialYear; private $baseYear; private $isAllDay; private $activeSet = array(); @@ -324,8 +322,6 @@ // TODO: Figure this out. $this->cursorWeek = null; $this->cursorDay = $date->getDay(); - $this->cursorDayMonth = $date->getMonth(); - $this->cursorDayYear = $date->getYear(); $this->cursorMonth = $date->getMonth(); $this->cursorYear = $date->getYear(); @@ -356,9 +352,19 @@ break; case self::FREQUENCY_MONTHLY: $this->cursorMonth -= $interval; + while ($this->cursorMonth < 1) { - $this->cursorYear--; - $this->cursorMonth += 12; + $this->rewindMonth(); + } + break; + case self::FREQUENCY_DAILY: + $this->cursorDay -= $interval; + + $week_start = $this->getWeekStart(); + while ($this->cursorDay < 1) { + $year_map = $this->getYearMap($this->cursorYear, $week_start); + $this->cursorDay += $year_map['monthDays'][$this->cursorMonth]; + $this->rewindMonth(); } break; default: @@ -373,9 +379,8 @@ $this->minimumEpoch = null; } - - $this->initialMonth = $this->cursorMonth; - $this->initialYear = $this->cursorYear; + $this->cursorDayMonth = $this->cursorMonth; + $this->cursorDayYear = $this->cursorYear; $by_hour = $this->getByHour(); $by_minute = $this->getByMinute(); @@ -1230,8 +1235,16 @@ if ($scale < self::SCALE_YEARLY) { $parts[] = $this->stateMonth; } + if ($scale < self::SCALE_MONTHLY) { + $parts[] = $this->stateDay; + } return implode('/', $parts); } + private function rewindMonth() { + $this->cursorYear--; + $this->cursorMonth += 12; + } + } diff --git a/src/parser/calendar/data/__tests__/PhutilCalendarRecurrenceRuleTestCase.php b/src/parser/calendar/data/__tests__/PhutilCalendarRecurrenceRuleTestCase.php --- a/src/parser/calendar/data/__tests__/PhutilCalendarRecurrenceRuleTestCase.php +++ b/src/parser/calendar/data/__tests__/PhutilCalendarRecurrenceRuleTestCase.php @@ -723,7 +723,7 @@ ); // This is testing that INTERVAL is respected in the presence of a BYMONTH - // filter which skips some months + // filter which skips some months. $tests[] = array( 'BYMONTH' => array(12), 'INTERVAL' => 17, @@ -734,7 +734,76 @@ '19981205', ); + $tests[] = array( + 'BYMONTHDAY' => array(1, 3), + ); + $expect[] = array( + '19970903', + '19971001', + '19971003', + ); + $tests[] = array( + 'BYMONTH' => array(1, 3), + 'BYMONTHDAY' => array(5, 7), + ); + $expect[] = array( + '19980105', + '19980107', + '19980305', + ); + + $tests[] = array( + 'BYDAY' => array('TU', 'TH'), + ); + $expect[] = array( + '19970902', + '19970904', + '19970909', + ); + + $tests[] = array( + 'BYMONTH' => array(1, 3), + 'BYDAY' => array('TU', 'TH'), + ); + $expect[] = array( + '19980101', + '19980106', + '19980108', + ); + + $tests[] = array( + 'BYMONTHDAY' => array(1, 3), + 'BYDAY' => array('TU', 'TH'), + ); + $expect[] = array( + '19980101', + '19980203', + '19980303', + ); + + $tests[] = array( + 'BYMONTH' => array(1, 3), + 'BYMONTHDAY' => array(1, 3), + 'BYDAY' => array('TU', 'TH'), + ); + $expect[] = array( + '19980101', + '19980303', + '20010301', + ); + + $tests[] = array( + 'BYHOUR' => array(6, 18), + 'BYMINUTE' => array(15, 45), + 'BYSETPOS' => array(3, -3), + 'DTSTART' => '19970902T090000Z', + ); + $expect[] = array( + '19970902T181500Z', + '19970903T064500Z', + '19970903T181500Z', + ); $this->assertRules( array(