/** * create a calendar timezone and standard/daylight components * * Result when 'Europe/Stockholm' and no from/to arguments is used as timezone: * * BEGIN:VTIMEZONE * TZID:Europe/Stockholm * BEGIN:STANDARD * DTSTART:20101031T020000 * TZOFFSETFROM:+0200 * TZOFFSETTO:+0100 * TZNAME:CET * END:STANDARD * BEGIN:DAYLIGHT * DTSTART:20100328T030000 * TZOFFSETFROM:+0100 * TZOFFSETTO:+0200 * TZNAME:CEST * END:DAYLIGHT * END:VTIMEZONE * * @author Kjell-Inge Gustafsson, kigkonsult <*****@*****.**> * @since 2.16.1 - 2012-11-26 * Generates components for all transitions in a date range, based on contribution by Yitzchok Lavi <*****@*****.**> * Additional changes jpirkey * @param object $calendar, reference to an iCalcreator calendar instance * @param string $timezone, a PHP5 (DateTimeZone) valid timezone * @param array $xProp, *[x-propName => x-propValue], optional * @param int $from a unix timestamp * @param int $to a unix timestamp * @return bool */ public static function createTimezone(&$calendar, $timezone, $xProp = array(), $from = null, $to = null) { if (empty($timezone)) { return FALSE; } if (!empty($from) && !is_int($from)) { return FALSE; } if (!empty($to) && !is_int($to)) { return FALSE; } try { $dtz = new \DateTimeZone($timezone); $transitions = $dtz->getTransitions(); $utcTz = new \DateTimeZone('UTC'); } catch (Exception $e) { return FALSE; } if (empty($to)) { $dates = array_keys($calendar->getProperty('dtstart')); if (empty($dates)) { $dates = array(date('Ymd')); } } if (!empty($from)) { $dateFrom = new \DateTime("@{$from}"); } else { $from = reset($dates); // set lowest date to the lowest dtstart date $dateFrom = new \DateTime($from . 'T000000', $dtz); $dateFrom->modify('-1 month'); // set $dateFrom to one month before the lowest date $dateFrom->setTimezone($utcTz); // convert local date to UTC } $dateFromYmd = $dateFrom->format('Y-m-d'); if (!empty($to)) { $dateTo = new \DateTime("@{$to}"); } else { $to = end($dates); // set highest date to the highest dtstart date $dateTo = new \DateTime($to . 'T235959', $dtz); $dateTo->modify('+1 year'); // set $dateTo to one year after the highest date $dateTo->setTimezone($utcTz); // convert local date to UTC } $dateToYmd = $dateTo->format('Y-m-d'); unset($dtz); $transTemp = array(); $prevOffsetfrom = 0; $stdIx = $dlghtIx = null; $prevTrans = FALSE; foreach ($transitions as $tix => $trans) { // all transitions in date-time order!! $date = new \DateTime("@{$trans['ts']}"); // set transition date (UTC) $transDateYmd = $date->format('Y-m-d'); if ($transDateYmd < $dateFromYmd) { $prevOffsetfrom = $trans['offset']; // previous trans offset will be 'next' trans offsetFrom $prevTrans = $trans; // save it in case we don't find any that match $prevTrans['offsetfrom'] = 0 < $tix ? $transitions[$tix - 1]['offset'] : 0; continue; } if ($transDateYmd > $dateToYmd) { break; } // loop always (?) breaks here if (!empty($prevOffsetfrom) || 0 == $prevOffsetfrom) { $trans['offsetfrom'] = $prevOffsetfrom; // i.e. set previous offsetto as offsetFrom $date->modify($trans['offsetfrom'] . 'seconds'); // convert utc date to local date $d = $date->format('Y-n-j-G-i-s'); // set date to array to ease up dtstart and (opt) rdate setting $d = explode('-', $d); $trans['time'] = array('year' => $d[0], 'month' => $d[1], 'day' => $d[2], 'hour' => $d[3], 'min' => $d[4], 'sec' => $d[5]); } $prevOffsetfrom = $trans['offset']; if (TRUE !== $trans['isdst']) { // standard timezone if (!empty($stdIx) && isset($transTemp[$stdIx]['offsetfrom']) && $transTemp[$stdIx]['abbr'] == $trans['abbr'] && $transTemp[$stdIx]['offsetfrom'] == $trans['offsetfrom'] && $transTemp[$stdIx]['offset'] == $trans['offset']) { $transTemp[$stdIx]['rdate'][] = $trans['time']; continue; } $stdIx = $tix; } else { // daylight timezone if (!empty($dlghtIx) && isset($transTemp[$dlghtIx]['offsetfrom']) && $transTemp[$dlghtIx]['abbr'] == $trans['abbr'] && $transTemp[$dlghtIx]['offsetfrom'] == $trans['offsetfrom'] && $transTemp[$dlghtIx]['offset'] == $trans['offset']) { $transTemp[$dlghtIx]['rdate'][] = $trans['time']; continue; } $dlghtIx = $tix; } // end daylight timezone $transTemp[$tix] = $trans; } // end foreach( $transitions as $tix => $trans ) $tz =& $calendar->newComponent('VTimeZone'); $tz->setproperty('tzid', $timezone); if (!empty($xProp)) { foreach ($xProp as $xPropName => $xPropValue) { if ('x-' == strtolower(substr($xPropName, 0, 2))) { $tz->setproperty($xPropName, $xPropValue); } } } if (empty($transTemp)) { // if no match found if ($prevTrans) { // then we use the last transition (before startdate) for the tz info $date = new \DateTime("@{$prevTrans['ts']}"); // set transition date (UTC) $date->modify($prevTrans['offsetfrom'] . 'seconds'); // convert utc date to local date $d = $date->format('Y-n-j-G-i-s'); // set date to array to ease up dtstart setting $d = explode('-', $d); $prevTrans['time'] = array('year' => $d[0], 'month' => $d[1], 'day' => $d[2], 'hour' => $d[3], 'min' => $d[4], 'sec' => $d[5]); $transTemp[0] = $prevTrans; } else { // or we use the timezone identifier to BUILD the standard tz info (?) $date = new \DateTime('now', new \DateTimeZone($timezone)); $transTemp[0] = array('time' => $date->format('Y-m-d\\TH:i:s O'), 'offset' => $date->format('Z'), 'offsetfrom' => $date->format('Z'), 'isdst' => FALSE); } } unset($transitions, $date, $prevTrans); foreach ($transTemp as $tix => $trans) { $type = TRUE !== $trans['isdst'] ? 'standard' : 'daylight'; $scomp =& $tz->newComponent($type); $scomp->setProperty('dtstart', $trans['time']); // $scomp->setProperty( 'x-utc-timestamp', $tix.' : '.$trans['ts'] ); // test ### if (!empty($trans['abbr'])) { $scomp->setProperty('tzname', $trans['abbr']); } if (isset($trans['offsetfrom'])) { $scomp->setProperty('tzoffsetfrom', ICalUtilityFunctions::offsetSec2His($trans['offsetfrom'])); } $scomp->setProperty('tzoffsetto', ICalUtilityFunctions::offsetSec2His($trans['offset'])); if (isset($trans['rdate'])) { $scomp->setProperty('RDATE', $trans['rdate']); } } return TRUE; }