209 lines
6.4 KiB
PHP
209 lines
6.4 KiB
PHP
<?php
|
|
/**
|
|
* Copyright 2011-2017 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 Jan Schneider <jan@horde.org>
|
|
* @package Timezone
|
|
*/
|
|
|
|
/**
|
|
* Class representing a set of "Rule" timezone database entries of the
|
|
* same name.
|
|
*
|
|
* @author Jan Schneider <jan@horde.org>
|
|
* @package Timezone
|
|
*/
|
|
class Horde_Timezone_Zone
|
|
{
|
|
/**
|
|
* The timezone ID.
|
|
*
|
|
* @var string
|
|
*/
|
|
protected $_name;
|
|
|
|
/**
|
|
* A Horde_Timezone object.
|
|
*
|
|
* @va Horde_Timezone
|
|
*/
|
|
protected $_tz;
|
|
|
|
/**
|
|
* Zone lines of this zone object.
|
|
*
|
|
* @var array
|
|
*/
|
|
protected $_info = array();
|
|
|
|
/**
|
|
* Constructor.
|
|
*
|
|
* @param string $name A timezone ID.
|
|
* @param Horde_Timezone $tz A Horde_Timezone object. Used to retrieve
|
|
* rules for this timezone.
|
|
*/
|
|
public function __construct($name, Horde_Timezone $tz)
|
|
{
|
|
$this->setTzid($name);
|
|
$this->_tz = $tz;
|
|
}
|
|
|
|
/**
|
|
* Sets the timezone ID.
|
|
*
|
|
* There are aliases for timezone IDs, it might be necessary to
|
|
* use one of those.
|
|
*
|
|
* @param string $name A timezone ID.
|
|
*/
|
|
public function setTzid($name)
|
|
{
|
|
$this->_name = $name;
|
|
}
|
|
|
|
/**
|
|
* Adds a Zone line to this zone object.
|
|
*
|
|
* @param array $info A parsed Zone line or continuation line.
|
|
*/
|
|
public function add($info)
|
|
{
|
|
$this->_info[] = $info;
|
|
}
|
|
|
|
/**
|
|
* Exports this zone to a VTIMEZONE component.
|
|
*
|
|
* @param Horde_Date $from The earliest date that we need timezone
|
|
* definitions for.
|
|
* @param Horde_Date $to The latest date.
|
|
*
|
|
* @return Horde_Icalendar_Vtimezone A VTIMEZONE component representing
|
|
* this timezone.
|
|
* @throws Horde_Timezone_Exception
|
|
*/
|
|
public function toVtimezone($from = null, $to = null)
|
|
{
|
|
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++) {
|
|
if ($to && $startDate->after($to)) {
|
|
continue;
|
|
}
|
|
$name = $this->_info[$i][2];
|
|
$endDate = count($this->_info[$i]) > 3 ? $this->_getDate($i) : null;
|
|
if ($from && $endDate && $endDate->before($from)) {
|
|
continue;
|
|
}
|
|
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,
|
|
$from && $from->after($startDate) ? $from : $startDate,
|
|
$to && $to->before($endDate) ? $to : $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;
|
|
}
|
|
|
|
/**
|
|
* Calculates a date from the date columns of a Zone line.
|
|
*
|
|
* @param integer $line A line number.
|
|
*
|
|
* @return Horde_Date The date of this line.
|
|
*/
|
|
protected function _getDate($line)
|
|
{
|
|
$date = array_slice($this->_info[$line], 3);
|
|
$year = $date[0];
|
|
$month = isset($date[1]) ? Horde_Timezone::getMonth($date[1]) : 1;
|
|
$day = isset($date[2]) ? $date[2] : 1;
|
|
$time = isset($date[3]) && $date[3] != '-' ? $date[3] : 0;
|
|
preg_match('/(\d+)(?::(\d+))?(?::(\d+))?(w|s|u)?/', $time, $match);
|
|
if (!isset($match[2])) {
|
|
$match[2] = 0;
|
|
}
|
|
if (!isset($match[3])) {
|
|
$match[3] = 0;
|
|
}
|
|
switch (substr($time, -1)) {
|
|
case 's':
|
|
// Standard time. Not sure what to do about this.
|
|
break;
|
|
case 'u':
|
|
// UTC, add offset.
|
|
$offset = $this->_getOffset($line);
|
|
$factor = $offset['ahead'] ? 1 : -1;
|
|
$match[1] += $factor * $offset['hour'];
|
|
$match[2] += $factor * $offset['minute'];
|
|
case 'w':
|
|
default:
|
|
// Wall time, nothing to do.
|
|
break;
|
|
}
|
|
return new Horde_Date(array('year' => $year,
|
|
'month' => $month,
|
|
'mday' => $day,
|
|
'hour' => $match[1],
|
|
'min' => $match[2],
|
|
'sec' => $match[3]));
|
|
}
|
|
|
|
/**
|
|
* Calculates an offset from the offset column of a Zone line.
|
|
*
|
|
* @param integer $line A line number.
|
|
*
|
|
* @return array A hash describing the offset.
|
|
*/
|
|
protected function _getOffset($line)
|
|
{
|
|
$offset = $this->_info[$line][0];
|
|
preg_match('/(-)?(\d+):(\d+)/', $offset, $match);
|
|
return array('ahead' => $match[1] != '-',
|
|
'hour' => $match[2],
|
|
'minute' => $match[3]);
|
|
}
|
|
}
|