--- Date/Recurrence.php.orig 2012-07-10 19:54:48.000000000 +0200 +++ Date/Recurrence.php 2012-07-10 19:55:38.000000000 +0200 @@ -95,6 +95,20 @@ public $recurData = null; /** + * BYDAY recurrence number + * + * @var integer + */ + public $recurNthDay = null; + + /** + * BYMONTH recurrence data + * + * @var array + */ + public $recurMonths = array(); + + /** * All the exceptions from recurrence for this event. * * @var array @@ -157,6 +171,44 @@ } /** + * + * @param integer $nthDay The nth weekday of month to repeat events on + */ + public function setRecurNthWeekday($nth) + { + $this->recurNthDay = (int)$nth; + } + + /** + * + * @return integer The nth weekday of month to repeat events. + */ + public function getRecurNthWeekday() + { + return isset($this->recurNthDay) ? $this->recurNthDay : ceil($this->start->mday / 7); + } + + /** + * Specifies the months for yearly (weekday) recurrence + * + * @param array $months List of months (integers) this event recurs on. + */ + function setRecurByMonth($months) + { + $this->recurMonths = (array)$months; + } + + /** + * Returns a list of months this yearly event recurs on + * + * @return array List of months (integers) this event recurs on. + */ + function getRecurByMonth() + { + return $this->recurMonths; + } + + /** * Returns the days this event recurs on. * * @return integer A mask consisting of Horde_Date::MASK_* constants @@ -546,8 +598,13 @@ $estart = clone $this->start; // What day of the week, and week of the month, do we recur on? - $nth = ceil($this->start->mday / 7); - $weekday = $estart->dayOfWeek(); + if (isset($this->recurNthDay)) { + $nth = $this->recurNthDay; + $weekday = log($this->recurData, 2); + } else { + $nth = ceil($this->start->mday / 7); + $weekday = $estart->dayOfWeek(); + } // Adjust $estart to be the first candidate. $offset = ($after->month - $estart->month) + ($after->year - $estart->year) * 12; @@ -660,8 +717,13 @@ $estart = clone $this->start; // What day of the week, and week of the month, do we recur on? - $nth = ceil($this->start->mday / 7); - $weekday = $estart->dayOfWeek(); + if (isset($this->recurNthDay)) { + $nth = $this->recurNthDay; + $weekday = log($this->recurData, 2); + } else { + $nth = ceil($this->start->mday / 7); + $weekday = $estart->dayOfWeek(); + } // Adjust $estart to be the first candidate. $offset = floor(($after->year - $estart->year + $this->recurInterval - 1) / $this->recurInterval) * $this->recurInterval; @@ -894,15 +956,6 @@ case 'W': $this->setRecurType(self::RECUR_WEEKLY); if (!empty($remainder)) { - $maskdays = array( - 'SU' => Horde_Date::MASK_SUNDAY, - 'MO' => Horde_Date::MASK_MONDAY, - 'TU' => Horde_Date::MASK_TUESDAY, - 'WE' => Horde_Date::MASK_WEDNESDAY, - 'TH' => Horde_Date::MASK_THURSDAY, - 'FR' => Horde_Date::MASK_FRIDAY, - 'SA' => Horde_Date::MASK_SATURDAY, - ); $mask = 0; while (preg_match('/^ ?[A-Z]{2} ?/', $remainder, $matches)) { $day = trim($matches[0]); @@ -953,7 +1006,10 @@ list($year, $month, $mday) = sscanf($remainder, '%04d%02d%02d'); $this->setRecurEnd(new Horde_Date(array('year' => $year, 'month' => $month, - 'mday' => $mday))); + 'mday' => $mday, + 'hour' => 23, + 'min' => 59, + 'sec' => 59))); } } } @@ -1049,6 +1105,16 @@ // Always default the recurInterval to 1. $this->setRecurInterval(isset($rdata['INTERVAL']) ? $rdata['INTERVAL'] : 1); + $maskdays = array( + 'SU' => Horde_Date::MASK_SUNDAY, + 'MO' => Horde_Date::MASK_MONDAY, + 'TU' => Horde_Date::MASK_TUESDAY, + 'WE' => Horde_Date::MASK_WEDNESDAY, + 'TH' => Horde_Date::MASK_THURSDAY, + 'FR' => Horde_Date::MASK_FRIDAY, + 'SA' => Horde_Date::MASK_SATURDAY, + ); + switch (Horde_String::upper($rdata['FREQ'])) { case 'DAILY': $this->setRecurType(self::RECUR_DAILY); @@ -1057,15 +1123,6 @@ case 'WEEKLY': $this->setRecurType(self::RECUR_WEEKLY); if (isset($rdata['BYDAY'])) { - $maskdays = array( - 'SU' => Horde_Date::MASK_SUNDAY, - 'MO' => Horde_Date::MASK_MONDAY, - 'TU' => Horde_Date::MASK_TUESDAY, - 'WE' => Horde_Date::MASK_WEDNESDAY, - 'TH' => Horde_Date::MASK_THURSDAY, - 'FR' => Horde_Date::MASK_FRIDAY, - 'SA' => Horde_Date::MASK_SATURDAY, - ); $days = explode(',', $rdata['BYDAY']); $mask = 0; foreach ($days as $day) { @@ -1090,6 +1147,10 @@ case 'MONTHLY': if (isset($rdata['BYDAY'])) { $this->setRecurType(self::RECUR_MONTHLY_WEEKDAY); + if (preg_match('/(-?[1-4])([A-Z]+)/', $rdata['BYDAY'], $m)) { + $this->setRecurOnDay($maskdays[$m[2]]); + $this->setRecurNthWeekday($m[1]); + } } else { $this->setRecurType(self::RECUR_MONTHLY_DATE); } @@ -1100,6 +1161,14 @@ $this->setRecurType(self::RECUR_YEARLY_DAY); } elseif (isset($rdata['BYDAY'])) { $this->setRecurType(self::RECUR_YEARLY_WEEKDAY); + if (preg_match('/(-?[1-4])([A-Z]+)/', $rdata['BYDAY'], $m)) { + $this->setRecurOnDay($maskdays[$m[2]]); + $this->setRecurNthWeekday($m[1]); + } + if ($rdata['BYMONTH']) { + $months = explode(',', $rdata['BYMONTH']); + $this->setRecurByMonth($months); + } } else { $this->setRecurType(self::RECUR_YEARLY_DATE); } @@ -1163,13 +1232,19 @@ break; case self::RECUR_MONTHLY_WEEKDAY: - $nth_weekday = (int)($this->start->mday / 7); - if (($this->start->mday % 7) > 0) { - $nth_weekday++; + if (isset($this->recurNthDay)) { + $nth_weekday = $this->recurNthDay; + $day_of_week = log($this->recurData, 2); + } else { + $day_of_week = $this->start->dayOfWeek(); + $nth_weekday = (int)($this->start->mday / 7); + if (($this->start->mday % 7) > 0) { + $nth_weekday++; + } } $vcaldays = array('SU', 'MO', 'TU', 'WE', 'TH', 'FR', 'SA'); $rrule = 'FREQ=MONTHLY;INTERVAL=' . $this->recurInterval - . ';BYDAY=' . $nth_weekday . $vcaldays[$this->start->dayOfWeek()]; + . ';BYDAY=' . $nth_weekday . $vcaldays[$day_of_week]; break; case self::RECUR_YEARLY_DATE: @@ -1182,15 +1257,22 @@ break; case self::RECUR_YEARLY_WEEKDAY: - $nth_weekday = (int)($this->start->mday / 7); - if (($this->start->mday % 7) > 0) { - $nth_weekday++; - } + if (isset($this->recurNthDay)) { + $nth_weekday = $this->recurNthDay; + $day_of_week = log($this->recurData, 2); + } else { + $day_of_week = $this->start->dayOfWeek(); + $nth_weekday = (int)($this->start->mday / 7); + if (($this->start->mday % 7) > 0) { + $nth_weekday++; + } + } + $months = !empty($this->recurMonths) ? join(',', $this->recurMonths) : $this->start->month; $vcaldays = array('SU', 'MO', 'TU', 'WE', 'TH', 'FR', 'SA'); $rrule = 'FREQ=YEARLY;INTERVAL=' . $this->recurInterval . ';BYDAY=' . $nth_weekday - . $vcaldays[$this->start->dayOfWeek()] + . $vcaldays[$day_of_week] . ';BYMONTH=' . $this->start->month; break; } @@ -1223,6 +1305,21 @@ $this->setRecurInterval((int)$hash['interval']); + $month2number = array( + 'january' => 1, + 'february' => 2, + 'march' => 3, + 'april' => 4, + 'may' => 5, + 'june' => 6, + 'july' => 7, + 'august' => 8, + 'september' => 9, + 'october' => 10, + 'november' => 11, + 'december' => 12, + ); + $parse_day = false; $set_daymask = false; $update_month = false; @@ -1255,11 +1352,9 @@ case 'weekday': $this->setRecurType(self::RECUR_MONTHLY_WEEKDAY); - $nth_weekday = (int)$hash['daynumber']; - $hash['daynumber'] = 1; + $this->setRecurNthWeekday($hash['daynumber']); $parse_day = true; - $update_daynumber = true; - $update_weekday = true; + $set_daymask = true; break; } break; @@ -1297,12 +1392,13 @@ } $this->setRecurType(self::RECUR_YEARLY_WEEKDAY); - $nth_weekday = (int)$hash['daynumber']; - $hash['daynumber'] = 1; + $this->setRecurNthWeekday($hash['daynumber']); $parse_day = true; - $update_month = true; - $update_daynumber = true; - $update_weekday = true; + $set_daymask = true; + + if ($hash['month'] && isset($month2number[$hash['month']])) { + $this->setRecurByMonth($month2number[$hash['month']]); + } break; } } @@ -1368,21 +1464,6 @@ if ($update_month || $update_daynumber || $update_weekday) { if ($update_month) { - $month2number = array( - 'january' => 1, - 'february' => 2, - 'march' => 3, - 'april' => 4, - 'may' => 5, - 'june' => 6, - 'july' => 7, - 'august' => 8, - 'september' => 9, - 'october' => 10, - 'november' => 11, - 'december' => 12, - ); - if (isset($month2number[$hash['month']])) { $this->start->month = $month2number[$hash['month']]; } @@ -1398,7 +1479,7 @@ } if ($update_weekday) { - $this->start->setNthWeekday($last_found_day, $nth_weekday); + $this->setNthWeekday($nth_weekday); } }