/** * create 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.11.8 - 2012-02-06 * Generates components for all transitions in a date range, based on contribution by Yitzchok Lavi <*****@*****.**> * @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 an unix timestamp * @param int $to an unix timestamp * @return bool */ public static function createTimezone(&$calendar, $timezone, $xProp = array(), $from = null, $to = null) { if (!class_exists('DateTimeZone')) { return FALSE; } if (empty($timezone)) { return FALSE; } try { $dtz = new DateTimeZone($timezone); $transitions = $dtz->getTransitions(); unset($dtz); $utcTz = new DateTimeZone('UTC'); } catch (Exception $e) { return FALSE; } if (empty($to)) { $dates = array_keys($calendar->getProperty('dtstart')); } $transCnt = 2; // number of transitions in output if empty input $from/$to and an empty dates-array $dateFrom = new DateTime('now'); $dateTo = new DateTime('now'); if (!empty($from)) { $dateFrom->setTimestamp($from); } else { if (!empty($dates)) { $dateFrom = new DateTime(reset($dates)); } // set lowest date to the lowest dtstart date $dateFrom->modify('-1 month'); // set $dateFrom to one month before the lowest date } $dateFrom->setTimezone($utcTz); // convert local date to UTC if (!empty($to)) { $dateTo->setTimestamp($to); } else { if (!empty($dates)) { $dateTo = new DateTime(end($dates)); // set highest date to the highest dtstart date $to = $dateTo->getTimestamp(); // set mark that a highest date is found } $dateTo->modify('+1 year'); // set $dateTo to one year after the highest date } $dateTo->setTimezone($utcTz); // convert local date to UTC $transTemp = array(); $prevOffsetfrom = $stdCnt = $dlghtCnt = 0; $stdIx = $dlghtIx = null; $date = new DateTime('now', $utcTz); foreach ($transitions as $tix => $trans) { // all transitions in date-time order!! $date->setTimestamp($trans['ts']); // set transition date (UTC) if ($date < $dateFrom) { $prevOffsetfrom = $trans['offset']; // previous trans offset will be 'next' trans offsetFrom continue; } if ($date > $dateTo) { 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 $trans['time'] = array('year' => $date->format('Y'), 'month' => $date->format('n'), 'day' => $date->format('j'), 'hour' => $date->format('G'), 'min' => $date->format('i'), 'sec' => $date->format('s')); } $prevOffsetfrom = $trans['offset']; $trans['prevYear'] = $trans['time']['year']; 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]['prevYear'] + 1 == $trans['time']['year']) { $transTemp[$stdIx]['prevYear'] = $trans['time']['year']; $transTemp[$stdIx]['rdate'][] = $trans['time']; continue; } $stdIx = $tix; $stdCnt += 1; } 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]['prevYear'] + 1 == $trans['time']['year']) { $transTemp[$dlghtIx]['prevYear'] = $trans['time']['year']; $transTemp[$dlghtIx]['rdate'][] = $trans['time']; continue; } $dlghtIx = $tix; $dlghtCnt += 1; } // end daylight timezone if (empty($to) && $transCnt == count($transTemp)) { // store only $transCnt transitions if (TRUE !== $transTemp[0]['isdst']) { $stdCnt -= 1; } else { $dlghtCnt -= 1; } array_shift($transTemp); } // end if( empty( $to ) && ( $transCnt == count( $transTemp ))) $transTemp[$tix] = $trans; } // end foreach( $transitions as $tix => $trans ) unset($transitions); if (empty($transTemp)) { return FALSE; } $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); } } } foreach ($transTemp as $trans) { $type = TRUE !== $trans['isdst'] ? 'standard' : 'daylight'; $scomp =& $tz->newComponent($type); $scomp->setProperty('dtstart', $trans['time']); // $scomp->setProperty( 'x-utc-timestamp', $trans['ts'] ); // test ### if (!empty($trans['abbr'])) { $scomp->setProperty('tzname', $trans['abbr']); } $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; }
/** * 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; }
/** * create (very simple) timezone and standard/daylight components * * Result when 'Europe/Stockholm' is used as timezone: * * BEGIN:VTIMEZONE * TZID:Europe/Stockholm * BEGIN:STANDARD * DTSTART:20101031T020000 * TZOFFSETFROM:+0200 * TZOFFSETTO:+0100 * RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10 * TZNAME:CET * END:STANDARD * BEGIN:DAYLIGHT * DTSTART:20100328T030000 * TZOFFSETFROM:+0100 * TZOFFSETTO:+0200 * RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=3 * TZNAME:CEST * END:DAYLIGHT * END:VTIMEZONE * * @author Kjell-Inge Gustafsson, kigkonsult <*****@*****.**> * @since 2.10.3 - 2011-08-01 * @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 * @return bool */ public static function createTimezone(&$calendar, $timezone, $xProp = array()) { if (!class_exists('DateTimeZone')) { return FALSE; } if (empty($timezone)) { return FALSE; } try { $dtz = new DateTimeZone($timezone); } catch (Exception $e) { return FALSE; } $stdDTSTART = $stdTZOFFSETTO = $stdTZOFFSETFROM = $stdTZNAME = $dlghtDTSTART = $dlghtTZOFFSETTO = $dlghtTZOFFSETFROM = $dlghtTZNAME = FALSE; $dateNow = new DateTime(); $transitions = $dtz->getTransitions(); foreach ($transitions as $trans) { if (FALSE === ($date = DateTime::createFromFormat('Y-m-d', substr($trans['time'], 0, 10)))) { continue; } if ($date > $dateNow) { break; } if (TRUE !== $trans['isdst']) { $stdDTSTART = $trans['time']; $stdTZOFFSETTO = $dlghtTZOFFSETFROM = iCalUtilityFunctions::offsetSec2His($trans['offset']); $stdTZNAME = $trans['abbr']; } else { $dlghtDTSTART = $trans['time']; $dlghtTZOFFSETTO = $stdTZOFFSETFROM = iCalUtilityFunctions::offsetSec2His($trans['offset']); $dlghtTZNAME = $trans['abbr']; } } if (!$stdDTSTART || !$stdTZOFFSETTO || !$stdTZOFFSETFROM) { return FALSE; } $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); } } } $std =& $tz->newComponent('standard'); $std->setProperty('dtstart', $stdDTSTART); if ($stdTZNAME) { $std->setProperty('tzname', $stdTZNAME); } $std->setProperty('tzoffsetto', $stdTZOFFSETTO); $std->setProperty('tzoffsetfrom', $stdTZOFFSETFROM); if ($stdTZOFFSETTO != $stdTZOFFSETFROM && FALSE === iCalUtilityFunctions::_setTZrrule($std)) { $std->setProperty('RRULE', array('FREQ' => 'YEARLY', 'BYDAY' => array('-1', 'DAY' => 'SU'), 'BYMONTH' => 10)); } if (!$dlghtDTSTART || !$dlghtTZOFFSETTO || !$dlghtTZOFFSETFROM || $dlghtTZOFFSETTO == $dlghtTZOFFSETFROM) { return TRUE; } $dlght =& $tz->newComponent('daylight'); $dlght->setProperty('dtstart', $dlghtDTSTART); if ($dlghtTZNAME) { $dlght->setProperty('tzname', $dlghtTZNAME); } $dlght->setProperty('tzoffsetto', $dlghtTZOFFSETTO); $dlght->setProperty('tzoffsetfrom', $dlghtTZOFFSETFROM); if (FALSE === iCalUtilityFunctions::_setTZrrule($dlght)) { $dlght->setProperty('RRULE', array('FREQ' => 'YEARLY', 'BYDAY' => array('-1', 'DAY' => 'SU'), 'BYMONTH' => 3)); } return TRUE; }