Copyright 2003-2016 Horde LLC (http://www.horde.org/) See the enclosed file COPYING for license information (LGPL). If you did not receive this file, see http://www.horde.org/licenses/lgpl21.
Author: Mike Cochrane (mike@graftonhall.co.nz)
Inheritance: extends Horde_Icalendar
Beispiel #1
0
 /**
  * Adds rules from this ruleset to a VTIMEZONE component.
  *
  * @param Horde_Icalendar_Vtimezone $tz  A VTIMEZONE component.
  * @param string $tzid                   The timezone ID of the component.
  * @param string $name                   A timezone name abbreviation.
  *                                       May contain a placeholder that is
  *                                       replaced the Rules' "Letter(s)"
  *                                       entry.
  * @param array $startOffset             An offset hash describing the
  *                                       base offset of a timezone.
  * @param Horde_Date $start              Start of the period to add rules
  *                                       for.
  * @param Horde_Date $end                End of the period to add rules
  *                                       for.
  */
 public function addRules(Horde_Icalendar_Vtimezone $tz, $tzid, $name, $startOffset, Horde_Date $start, Horde_Date $end = null)
 {
     $offset = $startOffset;
     foreach ($this->_rules as $rule) {
         $year = $rule[3];
         if ($year[0] == 'o') {
             // TO is "only"
             $rule[3] = $rule[2];
         }
         if ($rule[3][0] != 'm' && $rule[3] < $start->year) {
             // TO is not maximum and is before the searched period
             continue;
         }
         if ($end && $rule[2][0] != 'm' && $rule[2] > $end->year) {
             // FROM is not "minimum" and is after the searched period
             break;
         }
         if ($rule[2][0] != 'm' && $rule[2] < $start->year) {
             $rule[2] = $start->year;
         }
         if ($rule[8] == 0) {
             $component = new Horde_Icalendar_Standard();
             $component->setAttribute('TZOFFSETFROM', $offset);
             $component->setAttribute('TZOFFSETTO', $startOffset);
             $offset = $startOffset;
         } else {
             $component = new Horde_Icalendar_Daylight();
             $component->setAttribute('TZOFFSETFROM', $offset);
             $offset = $this->_getOffset($startOffset, $rule[8]);
             $component->setAttribute('TZOFFSETTO', $offset);
         }
         $month = Horde_Timezone::getMonth($rule[5]);
         // Retrieve time of rule start.
         preg_match('/(\\d+)(?::(\\d+))?(?::(\\d+))?(w|s|u)?/', $rule[7], $match);
         if (!isset($match[2])) {
             $match[2] = 0;
         }
         if ($rule[2] == $rule[3] && preg_match('/^\\d+$/', $rule[6])) {
             // Rule lasts only for a single year and starts on a specific
             // date.
             $rdate = new Horde_Date(array('year' => $rule[2], 'month' => Horde_Timezone::getMonth($rule[5]), 'mday' => $rule[6], 'hour' => $match[1], 'min' => $match[2], 'sec' => 0));
             $component->setAttribute('DTSTART', $rdate);
         } elseif (substr($rule[6], 0, 4) == 'last') {
             // Rule starts on the last of a certain weekday of the month.
             $weekday = $this->_weekdays[substr($rule[6], 4, 3)];
             $last = new Horde_Date(array('year' => $rule[2], 'month' => $month, 'mday' => Horde_Date_Utils::daysInMonth($month, $rule[2]), 'hour' => $match[1], 'min' => $match[2], 'sec' => 0));
             while ($last->dayOfWeek() != $weekday) {
                 $last->mday--;
             }
             $component->setAttribute('DTSTART', $last);
             if ($rule[3][0] == 'm') {
                 $until = '';
             } else {
                 $last = new Horde_Date(array('year' => $rule[3], 'month' => $month, 'mday' => Horde_Date_Utils::daysInMonth($month, $rule[2]), 'hour' => $match[1], 'min' => $match[2], 'sec' => 0), $tzid);
                 while ($last->dayOfWeek() != $weekday) {
                     $last->mday--;
                 }
                 $last->setTimezone('UTC');
                 $until = ';UNTIL=' . $last->format('Ymd\\THIs') . 'Z';
             }
             $component->setAttribute('RRULE', 'FREQ=YEARLY;BYDAY=-1' . Horde_String::upper(substr($rule[6], 4, 2)) . ';BYMONTH=' . $month . $until);
         } elseif (strpos($rule[6], '>=')) {
             // Rule starts on a certain weekday after a certain day of
             // month.
             list($weekday, $day) = explode('>=', $rule[6]);
             $weekdayInt = $this->_weekdays[substr($weekday, 0, 3)];
             $first = new Horde_Date(array('year' => $rule[2], 'month' => $month, 'mday' => $day, 'hour' => $match[1], 'min' => $match[2], 'sec' => 0));
             while ($first->dayOfWeek() != $weekdayInt) {
                 $first->mday++;
             }
             $component->setAttribute('DTSTART', $first);
             if ($rule[3][0] == 'm') {
                 $until = '';
             } else {
                 $last = new Horde_Date(array('year' => $rule[3], 'month' => $month, 'mday' => $day, 'hour' => $match[1], 'min' => $match[2], 'sec' => 0), $tzid);
                 while ($last->dayOfWeek() != $weekday) {
                     $last->mday++;
                 }
                 $last->setTimezone('UTC');
                 $until = ';UNTIL=' . $last->format('Ymd\\THIs') . 'Z';
             }
             for ($days = array(), $i = $day, $lastDay = min(Horde_Date_Utils::daysInMonth($month, $rule[2]), $i + 6); $day > 1 && $i <= $lastDay; $i++) {
                 $days[] = $i;
             }
             $component->setAttribute('RRULE', 'FREQ=YEARLY;BYMONTH=' . $month . ($days ? ';BYMONTHDAY=' . implode(',', $days) : '') . ';BYDAY=1' . Horde_String::upper(substr($weekday, 0, 2)) . $until);
         } elseif (strpos($rule[6], '<=')) {
             // Rule starts on a certain weekday before a certain day of
             // month.
             list($weekday, $day) = explode('>=', $rule[6]);
             $weekdayInt = $this->_weekdays[substr($weekday, 0, 3)];
             $last = new Horde_Date(array('year' => $rule[2], 'month' => $month, 'mday' => $day, 'hour' => $match[1], 'min' => $match[2], 'sec' => 0));
             while ($last->dayOfWeek() != $weekdayInt) {
                 $last->mday--;
             }
             $component->setAttribute('DTSTART', $last);
             if ($rule[3][0] == 'm') {
                 $until = '';
             } else {
                 $last = new Horde_Date(array('year' => $rule[3], 'month' => $month, 'mday' => $day, 'hour' => $match[1], 'min' => $match[2], 'sec' => 0), $tzid);
                 while ($last->dayOfWeek() != $weekday) {
                     $last->mday--;
                 }
                 $last->setTimezone('UTC');
                 $until = ';UNTIL=' . $last->format('Ymd\\THIs') . 'Z';
             }
             for ($days = array(), $i = 1; $i <= $day; $i++) {
                 $days[] = $i;
             }
             $component->setAttribute('RRULE', 'FREQ=YEARLY;BYMONTH=' . $month . ';BYMONTHDAY=' . implode(',', $days) . ';BYDAY=-1' . Horde_String::upper(substr($weekday, 0, 2)) . $until);
         }
         $component->setAttribute('TZNAME', sprintf($name, $rule[9]));
         $tz->addComponent($component);
     }
 }
Beispiel #2
0
 /**
  * Parses child components of vTimezone component.
  *
  * Returns an array with the exact time of the time change as well as the
  * 'from' and 'to' offsets around the change. Time is arbitrarily based on
  * UTC for comparison.
  *
  * @param Horde_Icalendar_Standard|Horde_Icalendar_Daylight $child
  *        A timezone component.
  * @param integer $year
  *        The latest year we are interested in.
  *
  * @return array  A list of hashes with "time", "from", and "to" elements.
  */
 public function parseChild($child, $year)
 {
     $result['time'] = $result['end'] = 0;
     try {
         $t = $child->getAttribute('TZOFFSETFROM');
     } catch (Horde_Icalendar_Exception $e) {
         return array();
     }
     $result['from'] = ($t['hour'] * 60 * 60 + $t['minute'] * 60) * ($t['ahead'] ? 1 : -1);
     try {
         $t = $child->getAttribute('TZOFFSETTO');
     } catch (Horde_Icalendar_Exception $e) {
         return array();
     }
     $result['to'] = ($t['hour'] * 60 * 60 + $t['minute'] * 60) * ($t['ahead'] ? 1 : -1);
     try {
         $start = $child->getAttribute('DTSTART');
     } catch (Horde_Icalendar_Exception $e) {
         return array();
     }
     if (!is_int($start)) {
         return array();
     }
     $start = getdate($start);
     if ($start['year'] > $year) {
         return array();
     }
     $results = array();
     try {
         $rdates = $child->getAttributeValues('RDATE');
         foreach ($rdates as $rdate) {
             if ($rdate['year'] == $year || $rdate['year'] == $year - 1) {
                 $result['time'] = $result['end'] = gmmktime($start['hours'], $start['minutes'], $start['seconds'], $rdate['month'], $rdate['mday'], $rdate['year']);
                 $results[] = $result;
             }
         }
     } catch (Horde_Icalendar_Exception $e) {
     }
     try {
         $rrules = $child->getAttribute('RRULE');
     } catch (Horde_Icalendar_Exception $e) {
         if (!$results) {
             $result['time'] = $result['end'] = $start[0];
             $results[] = $result;
         }
         return $results;
     }
     $rrules = explode(';', $rrules);
     foreach ($rrules as $rrule) {
         $t = explode('=', $rrule);
         switch ($t[0]) {
             case 'FREQ':
                 if ($t[1] != 'YEARLY') {
                     return array();
                 }
                 break;
             case 'INTERVAL':
                 if ($t[1] != '1') {
                     return array();
                 }
                 break;
             case 'BYMONTH':
                 $month = intval($t[1]);
                 break;
             case 'BYDAY':
                 $len = strspn($t[1], '1234567890-+');
                 if ($len == 0) {
                     return array();
                 }
                 $weekday = substr($t[1], $len);
                 $weekdays = array('SU' => 0, 'MO' => 1, 'TU' => 2, 'WE' => 3, 'TH' => 4, 'FR' => 5, 'SA' => 6);
                 $weekday = $weekdays[$weekday];
                 $which = intval(substr($t[1], 0, $len));
                 break;
             case 'UNTIL':
                 $result['end'] = intval(substr($t[1], 0, 4));
                 break;
         }
     }
     if (empty($month) || !isset($weekday)) {
         return array();
     }
     // Get the timestamp for the first day of $month.
     $when = gmmktime($start['hours'], $start['minutes'], $start['seconds'], $month, 1, $year);
     // Get the day of the week for the first day of $month.
     $first_of_month_weekday = intval(gmstrftime('%w', $when));
     // Go to the first $weekday before first day of $month.
     if ($weekday >= $first_of_month_weekday) {
         $weekday -= 7;
     }
     $when -= ($first_of_month_weekday - $weekday) * 60 * 60 * 24;
     // If going backwards go to the first $weekday after last day
     // of $month.
     if ($which < 0) {
         do {
             $when += 60 * 60 * 24 * 7;
         } while (intval(gmstrftime('%m', $when)) == $month);
     }
     // Calculate $weekday number $which.
     $when += $which * 60 * 60 * 24 * 7;
     $result['time'] = $when;
     $results[] = $result;
     return $results;
 }
Beispiel #3
0
 /**
  * Exports this zone to a VTIMEZONE component.
  *
  * @return Horde_Icalendar_Vtimezone  A VTIMEZONE component representing
  *                                    this timezone.
  * @throws Horde_Timezone_Exception
  */
 public function toVtimezone()
 {
     if (!count($this->_info)) {
         throw new Horde_Timezone_Exception('No rules found for timezone ' . $this->_name);
     }
     $tz = new Horde_Icalendar_Vtimezone();
     $tz->setAttribute('TZID', $this->_name);
     if (count($this->_info[0]) <= 3) {
         // Zone has no start or end date, but DAYLIGHT and STANDARD
         // components are required to have a DTSTART attribute [RFC
         // 5545 3.8.2.4].
         return $tz;
     }
     $startDate = $this->_getDate(0);
     $startOffset = $this->_getOffset(0);
     for ($i = 1, $c = count($this->_info); $i < $c; $i++) {
         $name = $this->_info[$i][2];
         $endDate = count($this->_info[$i]) > 3 ? $this->_getDate($i) : null;
         if ($this->_info[$i][1] == '-') {
             // Standard time.
             $component = new Horde_Icalendar_Standard();
         } elseif (preg_match('/\\d+(:(\\d+))?/', $this->_info[$i][1])) {
             // Indiviual rule not matching any ruleset.
             $component = new Horde_Icalendar_Daylight();
         } else {
             // Represented by a ruleset.
             $startOffset = $this->_getOffset($i);
             $this->_tz->getRule($this->_info[$i][1])->addRules($tz, $this->_name, $name, $startOffset, $startDate, $endDate);
             $startDate = $endDate;
             // Continue, because addRules() already adds the
             // component to $tz.
             continue;
         }
         $component->setAttribute('DTSTART', $startDate);
         $component->setAttribute('TZOFFSETFROM', $startOffset);
         $startOffset = $this->_getOffset($i);
         $component->setAttribute('TZOFFSETTO', $startOffset);
         $component->setAttribute('TZNAME', $name);
         $tz->addComponent($component);
     }
     return $tz;
 }
Beispiel #4
0
 /**
  * Adds rules from this ruleset to a VTIMEZONE component.
  *
  * @param Horde_Icalendar_Vtimezone $tz  A VTIMEZONE component.
  * @param string $tzid                   The timezone ID of the component.
  * @param string $name                   A timezone name abbreviation.
  *                                       May contain a placeholder that is
  *                                       replaced the Rules' "Letter(s)"
  *                                       entry.
  * @param array $startOffset             An offset hash describing the
  *                                       base offset of a timezone.
  * @param Horde_Date $start              Start of the period to add rules
  *                                       for.
  * @param Horde_Date $end                End of the period to add rules
  *                                       for.
  */
 public function addRules(Horde_Icalendar_Vtimezone $tz, $tzid, $name, $startOffset, Horde_Date $start, Horde_Date $end = null)
 {
     foreach ($this->_rules as $ruleNo => $rule) {
         // The rule items are:
         // 0: "Rule"
         // 1: The rule name
         // 2: The start year or "minimum"
         // 3: The end year or "only" if only at the start year or "maximum"
         // 4: Type, whatever that means (unused)
         // 5: The start month
         // 6: The start day, either specific or as a rule
         // 7: The start time
         // 8: The offset to the base time
         // 9: The time name abbreviation
         $year = $rule[3];
         if ($year[0] == 'o') {
             // TO is "only"
             $rule[3] = $rule[2];
         }
         if ($rule[3][0] != 'm' && $rule[3] < $start->year) {
             // TO is not "maximum" and is before the searched period
             continue;
         }
         if ($end && $rule[2][0] != 'm' && $rule[2] > $end->year) {
             // FROM is not "minimum" and is after the searched period
             break;
         }
         if ($rule[2][0] != 'm' && $rule[2] < $start->year) {
             $rule[2] = $start->year;
         }
         // The month of rule start.
         $month = Horde_Timezone::getMonth($rule[5]);
         // The time of rule start.
         preg_match('/(\\d+)(?::(\\d+))?(?::(\\d+))?([wsguz])?/', $rule[7], $match);
         $hour = $match[1];
         $minute = isset($match[2]) ? $match[2] : 0;
         if (!isset($match[4])) {
             $modifier = 'w';
         } elseif ($match[4] == 'g' || $match[4] == 'z') {
             $modifier = 'u';
         } else {
             $modifier = $match[4];
         }
         // Find the start date.
         $first = $this->_getFirstMatch($rule, $rule[2]);
         $first->hour = $hour;
         $first->min = $minute;
         $previousOffset = $this->_findPreviousOffset($first, $ruleNo, $startOffset);
         if ($rule[8] == 0) {
             $component = new Horde_Icalendar_Standard();
             $component->setAttribute('TZOFFSETFROM', $previousOffset);
             $component->setAttribute('TZOFFSETTO', $startOffset);
         } else {
             $component = new Horde_Icalendar_Daylight();
             $component->setAttribute('TZOFFSETFROM', $previousOffset);
             $component->setAttribute('TZOFFSETTO', $this->_getOffset($startOffset, $rule[8]));
         }
         switch ($modifier) {
             case 's':
                 $first->hour += ($previousOffset['ahead'] ? 1 : -1) * $previousOffset['hour'] - ($startOffset['ahead'] ? 1 : -1) * $startOffset['hour'];
                 $first->min += ($previousOffset['ahead'] ? 1 : -1) * $previousOffset['minute'] - ($startOffset['ahead'] ? 1 : -1) * $startOffset['minute'];
                 break;
             case 'u':
                 $first->hour += ($previousOffset['ahead'] ? 1 : -1) * $previousOffset['hour'];
                 $first->min += ($previousOffset['ahead'] ? 1 : -1) * $previousOffset['minute'];
                 break;
         }
         $component->setAttribute('DTSTART', $first);
         // Find the end date.
         if ($rule[3][0] == 'm') {
             $until = '';
         } else {
             $last = $this->_getFirstMatch($rule, $rule[3]);
             $last->hour = $hour;
             $last->min = $minute;
             switch ($modifier) {
                 case 's':
                     $last->hour -= ($startOffset['ahead'] ? 1 : -1) * $startOffset['hour'];
                     $last->min -= ($startOffset['ahead'] ? 1 : -1) * $startOffset['minute'];
                     break;
                 case 'w':
                     $last->hour -= ($previousOffset['ahead'] ? 1 : -1) * $previousOffset['hour'];
                     $last->min -= ($previousOffset['ahead'] ? 1 : -1) * $previousOffset['minute'];
                     break;
             }
             $until = ';UNTIL=' . $last->format('Ymd\\THis') . 'Z';
         }
         if ($rule[2] != $rule[3]) {
             if (preg_match('/^\\d+$/', $rule[6])) {
                 // Rule starts on a specific date.
                 $component->setAttribute('RRULE', 'FREQ=YEARLY;BYMONTH=' . $month . ';BYMONTHDAY=' . $rule[6] . $until);
             } elseif (substr($rule[6], 0, 4) == 'last') {
                 // Rule starts on the last of a certain weekday of the month.
                 $component->setAttribute('RRULE', 'FREQ=YEARLY;BYDAY=-1' . Horde_String::upper(substr($rule[6], 4, 2)) . ';BYMONTH=' . $month . $until);
             } elseif (strpos($rule[6], '>=')) {
                 // Rule starts on a certain weekday after a certain day of
                 // month.
                 list($weekday, $day) = explode('>=', $rule[6]);
                 for ($days = array(), $i = $day, $lastDay = min(Horde_Date_Utils::daysInMonth($month, $rule[2]), $i + 6); $day > 1 && $i <= $lastDay; $i++) {
                     $days[] = $i;
                 }
                 $component->setAttribute('RRULE', 'FREQ=YEARLY;BYMONTH=' . $month . ($days ? ';BYMONTHDAY=' . implode(',', $days) : '') . ';BYDAY=1' . Horde_String::upper(substr($weekday, 0, 2)) . $until);
             } elseif (strpos($rule[6], '<=')) {
                 // Rule starts on a certain weekday before a certain day of
                 // month.
                 for ($days = array(), $i = 1; $i <= $day; $i++) {
                     $days[] = $i;
                 }
                 $component->setAttribute('RRULE', 'FREQ=YEARLY;BYMONTH=' . $month . ';BYMONTHDAY=' . implode(',', $days) . ';BYDAY=-1' . Horde_String::upper(substr($weekday, 0, 2)) . $until);
             } else {
                 continue;
             }
         }
         $component->setAttribute('TZNAME', sprintf($name, $rule[9]));
         $tz->addComponent($component);
     }
 }