<?php
// {{{ Header
/*
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+
| SIERRA : PHP Application Framework http://code.google.com/p/sierra-php |
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+
| Copyright 2005 Jason Read |
| |
| Licensed under the Apache License, Version 2.0 (the "License"); |
| you may not use this file except in compliance with the License. |
| You may obtain a copy of the License at |
| |
| http://www.apache.org/licenses/LICENSE-2.0 |
| |
| Unless required by applicable law or agreed to in writing, software |
| distributed under the License is distributed on an "AS IS" BASIS, |
| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.|
| See the License for the specific language governing permissions and |
| limitations under the License. |
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+
*/
// }}}
// {{{ Includes
// }}}
// {{{ Constants
/**
* the linux clock configuration
* @type string
*/
define('SRA_TIME_ZONE_CLOCK_CONFIG', '/etc/sysconfig/clock');
/**
* the identifier for the GMT time zone
* @type string
*/
define('SRA_TIME_ZONE_GMT', 'GMT');
/**
* the timezone file used by the operating system
* @type string
*/
define('SRA_TIME_ZONE_SYSTEM', '/etc/localtime');
/**
* the identifier for the UTC time zone
* @type string
*/
define('SRA_TIME_ZONE_UTC', 'UTC');
/**
* the path to use (in addition to the standard path) when looking for the zdump
* utility used to determine timezone ranges
* @type string
*/
define('SRA_TIME_ZONE_ZDUMP_PATH', '/sbin:/usr/sbin:/usr/local/sbin');
/**
* the path to the zoneinfo directory
* @type string
*/
define('SRA_TIME_ZONE_ZONEINFO_PATH', '/usr/share/zoneinfo');
/**
* the prefix to use for time zone cache files
* @type string
*/
define('SRA_TIME_ZONE_ZONE_CACHE_PREFIX', '.tz-');
/**
* the path to the zone.tab file
* @type string
*/
define('SRA_TIME_ZONE_ZONETAB_PATH', SRA_TIME_ZONE_ZONEINFO_PATH . '/zone.tab');
// }}}
// {{{ SRA_TimeZone
/**
* The SRA_TimeZone class manages data and behavior pertaining to a time zone
* including interraction with SRA_GregorianDate
* @author Jason Read <hide@address.com>
* @package sierra.util
*/
class SRA_TimeZone {
// {{{ attributes
/**
* time zone identifier (i.e. America/Boise)
* @type string
*/
var $_id;
/**
* the standard abbreviation for this time zone
* @type string
*/
var $_abbr;
/**
* the dst abbreviation for this time zone (if $_dst is TRUE)
* @type string
*/
var $_abbrDst;
/**
* an optional time zone comment as specified in zone.tab
* @type string
*/
var $_comment;
/**
* the ISO 3166 2-character country code that this time zone pertains to
* @type string
*/
var $_country;
/**
* whether or not this time zone uses daylight savings
* @type boolean
*/
var $_dst = FALSE;
/**
* time zone latitude in ISO 6709 sign-degrees-minutes-seconds format, either
* +-DDMM or +-DDMMSS
* @type string
*/
var $_latitude;
/**
* time zone longitude in ISO 6709 sign-degrees-minutes-seconds format, either
* +-DDMM or +-DDMMSS
* @type string
*/
var $_longitude;
/**
* an array of hashes identifying the ranges when this time zone observes and
* does not observe daylight savings time. this hash will be ordered by start
* time and contains the following keys:
* start : YmdHis representing the start time for this range
* end : YmdHis representing the end time for this range
* dst : boolean - whether or not this is a dst range
* offset: the GMT offset in seconds for this range
* @type array
*/
var $_ranges;
/**
* the absolute path to the zone file for this timezone (located in
* SRA_TIME_ZONE_ZONEINFO_PATH)
* @type string
*/
var $_zoneFile;
// }}}
// {{{ SRA_TimeZone
/**
* this constructor should never be called outside of this class. Instead,
* use the singleton getTimeZone method
* @param array $attrs optional array of initization attributes
* @access private
*/
function SRA_TimeZone($attrs=NULL) {
if ($attrs && is_array($attrs)) {
if (isset($attrs['id'])) $this->_id = $attrs['id'];
if (isset($attrs['abbr'])) $this->_abbr = $attrs['abbr'];
if (isset($attrs['abbrDst'])) $this->_abbrDst = $attrs['abbrDst'];
if (isset($attrs['comment'])) $this->_comment = $attrs['comment'];
if (isset($attrs['country'])) $this->_country = $attrs['country'];
if (isset($attrs['dst'])) $this->_dst = $attrs['dst'];
if (isset($attrs['latitude'])) $this->_latitude = $attrs['latitude'];
if (isset($attrs['longitude'])) $this->_longitude = $attrs['longitude'];
if (isset($attrs['ranges'])) $this->_ranges = $attrs['ranges'];
if (isset($attrs['zoneFile'])) $this->_zoneFile = $attrs['zoneFile'];
}
}
// }}}
// {{{ copy
/**
* this method returns a copy of this SRA_TimeZone object
* @access public
* @return SRA_TimeZone
*/
function ©() {
$tz = new SRA_TimeZone();
$tz->_id = $this->_id;
$tz->_abbr = $this->_abbr;
$tz->_abbrDst = $this->_abbrDst;
$tz->_comment = $this->_comment;
$tz->_country = $this->_country;
$tz->_dst = $this->_dst;
$tz->_latitude = $this->_latitude;
$tz->_longitude = $this->_longitude;
$tz->_zoneFile = $this->_zoneFile;
return $tz;
}
// }}}
// {{{ equals
/**
* returns TRUE if this SRA_TimeZone is the same as $tz
* @param mixed $tz the time zone to compare with
* @access public
* @return boolean
*/
function equals(&$tz) {
return SRA_TimeZone::isValid($tz) && $this->getId() == $tz->getId();
}
// }}}
// {{{ getAbbr
/**
* returns the abbreviation for this timezone and $date
* @param SRA_GregorianDate $data the date to return the timezone for. it not
* specified, the non-dst (if applicable) abbreviation will be returned
* @access public
* @return string
*/
function getAbbr(&$date) {
return $this->_dst && SRA_GregorianDate::isValid($date) && $this->isDaylightSavings($date) ? $this->_abbrDst : $this->_abbr;
}
// }}}
// {{{ getComment
/**
* returns the value of the _comment attribute
* @access public
* @return string
*/
function getComment() {
return $this->_comment;
}
// }}}
// {{{ getCountry
/**
* returns the value of the _country attribute
* @access public
* @return string
*/
function getCountry() {
return $this->_country;
}
// }}}
// {{{ getDaylightSavingsEnd
/**
* returns an SRA_GregorianDate object representing the exact date/time when
* daylight savings ends for this time zone (when applicable, otherwise
* returns NULL)
* @param int $year the year to evaluate. if not specified, the current year
* will be used
* @access public
* @return SRA_GregorianDate
*/
function getDaylightSavingsEnd($year=NULL) {
$year = $year ? $year : date('Y');
if ($this->_dst && ($ranges =& $this->getRanges())) {
foreach(array_keys($ranges) as $key) {
if ($ranges[$key]['dst'] && $year == substr($ranges[$key]['end'], 0, 4)) {
return new SRA_GregorianDate($ranges[$key]['end']);
}
}
}
return NULL;
}
// }}}
// {{{ getDaylightSavingsStart
/**
* returns an SRA_GregorianDate object representing the exact date/time when
* daylight savings begins for this time zone (when applicable, otherwise
* returns NULL)
* @param int $year the year to evaluate. if not specified, the current year
* will be used
* @access public
* @return SRA_GregorianDate
*/
function getDaylightSavingsStart($year=NULL) {
$year = $year ? $year : date('Y');
if ($this->_dst && ($ranges =& $this->getRanges())) {
foreach(array_keys($ranges) as $key) {
if ($ranges[$key]['dst'] && $year == substr($ranges[$key]['start'], 0, 4)) {
return new SRA_GregorianDate($ranges[$key]['start']);
}
}
}
return NULL;
}
// }}}
// {{{ getGmtHourOffset
/**
* returns GMT offset (above) in hours (including decimals)
* @param mixed $date SRA_GregorianDate to consider for daylight savings
* (optional)
* @access public
* @return int
*/
function getGmtHourOffset(&$date) {
return $this->getGmtOffset($date)/60;
}
// }}}
// {{{ getGmtOffset
/**
* returns the difference in minutes between the this timezone and the GMT
* timezone for $date. this value may be positive or negative
* @param mixed $date SRA_GregorianDate to return the offset for. if not
* specified, the current date will be assumed
* @access public
* @return int
*/
function getGmtOffset(&$date=NULL) {
return $this->getSecGmtOffset($date)/60;
}
// }}}
// {{{ getSecGmtOffset
/**
* returns the difference in seconds between the this timezone and the GMT
* timezone for $date. this value may be positive or negative
* @param mixed $date SRA_GregorianDate to return the offset for. if not
* specified, the current date will be assumed
* @access public
* @return int
*/
function getSecGmtOffset(&$date=NULL) {
if ($range =& $this->getRange($date)) {
return $range['offset'];
}
}
// }}}
// {{{ getGmtOffsetForYear
/**
* returns the gmt offset to use for $year
* @param int $year the year to return the offset for. if not specified, the
* current year will be assumed
* @param boolean $dst whether or not to return the $dst offset for that year
* @access public
* @return hash
*/
function getGmtOffsetForYear($year=NULL, $dst=FALSE) {
if (!$year) { $year = date('Y'); }
if ($ranges =& $this->getRanges()) {
foreach(array_keys($ranges) as $key) {
if (((!$dst && !$ranges[$key]['dst']) || ($dst && $ranges[$key]['dst'])) && $year == substr($ranges[$key]['end'], 0, 4)) {
return $ranges[$key]['offset']/60;
}
}
}
return NULL;
}
// }}}
// {{{ getGmtOffsetString
/**
* this method returns the GMT offset for this time zone as a string using the
* format +/-HHMM (example: -0200 or +0700). if the $date parameter is
* provided, the offset will take into account that date and apply the
* necessary daylight savings differential (when that date falls within a
* daylight savings period). otherwise, regardless of whether or not the time
* zone uses daylight savings, the standard offset (non-daylight savings
* adjusted) will be returned
* @param mixed $date SRA_GregorianDate to consider for
* daylight savings (optional)
* @access public
* @return string
*/
function getGmtOffsetString(&$date) {
$offset=$this->getGmtOffset($date);
return ($offset > 0 ? '+' : '-') . sprintf('%02d', floor(abs($offset)/60)) . sprintf('%02d', abs($offset)%60);
}
// }}}
// {{{ getGmtOffsetStringISO8601
/**
* this method returns the GMT offset for this time zone as an ISO 8601
* compliant string using the format +/-HH:MM (example: -02:00 or +07:00).
* this is basically the same as 'getOffsetString' but it has a colon dividing
* hours and minutes. if the $date parameter is provided, the offset will take
* into account that date and apply the necessary daylight savings
* differential (when that date falls within a daylight savings period).
* otherwise, regardless of whether or not the time zone uses daylight
* savings, the standard offset (non-daylight savings adjusted) will be
* returned
* @param mixed $date SRA_GregorianDate to consider for
* daylight savings (optional)
* @access public
* @return string
*/
function getGmtOffsetStringISO8601(&$date) {
$offset=$this->getGmtOffset($date);
return ($offset > 0 ? '+' : '-') . sprintf('%02d', floor(abs($offset)/60)) . ':' . sprintf('%02d', abs($offset)%60);
}
// }}}
// {{{ getId
/**
* returns the value of the _id attribute
* @access public
* @return string
*/
function getId() {
return $this->_id;
}
// }}}
// {{{ getLatitude
/**
* returns the value of the _latitude attribute
* @access public
* @return string
*/
function getLatitude() {
return $this->_latitude;
}
// }}}
// {{{ getLongitude
/**
* returns the value of the _longitude attribute
* @access public
* @return string
*/
function getLongitude() {
return $this->_longitude;
}
// }}}
// {{{ getRange
/**
* returns the range associated with $date. this will be a hash with the
* following keys:
* start : YmdHis representing the start time for this range
* end : YmdHis representing the end time for this range
* dst : boolean - whether or not this is a dst range
* offset: the GMT offset in seconds for this range
* @param SRA_GregorianDate $date the date to return the range for. if not
* specified, the current time will be assumed
* @access public
* @return hash
*/
function getRange(&$date) {
if (!$date) { $date = new SRA_GregorianDate(); }
$compare = $date->format('YmdHis');
if ($ranges =& $this->getRanges()) {
foreach(array_keys($ranges) as $key) {
if ($next) { return $ranges[$key]; }
if ($compare >= $ranges[$key]['start'] && $compare <= $ranges[$key]['end']) {
if ($ranges[$key]['dst'] && $date->format('YmdH') == substr($ranges[$key]['end'], 0, 10) && $date->_dstOverlap) {
$next = TRUE;
}
else {
return $ranges[$key];
}
}
}
}
return NULL;
}
// }}}
// {{{ getRanges
/**
* returns the value of the _ranges attribute. the first time this method is
* invoked, those values will be unserialized from the ranges cache file
* @access public
* @return array
*/
function &getRanges() {
if (!isset($this->_ranges)) {
$cfile = $this->_getRangesCacheFile();
if (file_exists($cfile)) {
include($cfile);
}
if (!is_array($this->_ranges)) {
$msg = 'SRA_TimeZone::getRanges: Error - Unable to get ranges from cache file "' . $cfile . '"';
SRA_Error::logError($msg, __FILE__, __LINE__);
}
}
return $this->_ranges;
}
// }}}
// {{{ getZoneFile
/**
* returns the value of the _zoneFile attribute
* @access public
* @return string
*/
function getZoneFile() {
return $this->_zoneFile;
}
// }}}
// {{{ isDaylightSavings
/**
* returns TRUE if this time zone uses daylight savings. if the $date
* parameter is provided, the method will take into account that date and
* return TRUE ONLY if the time zone uses daylight savings AND that date falls
* within a daylight savings period
* @param mixed $date SRA_GregorianDate to consider for
* daylight savings (optional)
* @access public
* @return boolean
*/
function isDaylightSavings(&$date) {
return $this->_dst && SRA_GregorianDate::isValid($date) ?
$date->compare($start = $this->getDaylightSavingsStart($date->getYear())) >= 0 && $date->compare($end = $this->getDaylightSavingsEnd($date->getYear())) <= 0 && (!$date->_dstOverlap || $date->format('YmdH') != $end->format('YmdH')) :
$this->_dst;
}
// }}}
// {{{ _getRangesCacheFile
/**
* returns the path of the file used to cache the ranges for this time zone
* @param string $id allows this method to be invoked statically
* @access public
* @return string
*/
function _getRangesCacheFile($id=NULL) {
return SRA_Controller::getSysTmpDir() . '/' . SRA_TIME_ZONE_ZONE_CACHE_PREFIX . 'ranges-' . str_replace('/', '_', $id ? $id : $this->_id) . '.' . SRA_SYS_PHP_EXTENSION;
}
// }}}
// static methods
// {{{ getAllTimeZones
/**
* returns an array of all of the names of the time zones currently available
* in the operating system
* @param mixed $country a single 2-character ISO 3166 country code, or an
* array of country codes to use to filter the timezones returned. if
* specified, only the time zones for that country will be returned. set this
* parameter to 1 to use SRA_Controller::getAppDefaultCountry
* @param boolean $hash whether or not to return the options array as a hash
* instead of an array where the hash key is also the timezone identifier
* @param boolean $clearCache set to TRUE for force clearing of the zone cache
* @access public
* @return string[]
*/
function &getAllTimeZones($country=NULL, $hash=FALSE, $clearCache=FALSE) {
static $cachedTimeZones = array();
if ($country === 1) $country = SRA_Controller::getAppDefaultCountry();
$cacheKey = ($country ? $country : 'all') . '_' . $hash;
if (array_key_exists($cacheKey, $cachedTimeZones) && !$clearCache) {
return $cachedTimeZones[$cacheKey];
}
$ztCache = SRA_Controller::getSysTmpDir() . '/' . SRA_TIME_ZONE_ZONE_CACHE_PREFIX . 'zones';
if ($clearCache || !file_exists($ztCache) || SRA_File::compareMTimes($ztCache, SRA_TIME_ZONE_ZONETAB_PATH) != 1) {
$countries = array();
$tzcache = array();
foreach(file(SRA_TIME_ZONE_ZONETAB_PATH) as $line) {
if (preg_match('/^([A-Z]{2})[\s]+([\+\-][0-9]+)([\+\-][0-9]+)[\s]+([\S]+)([\s]+[\S][\s\S]*)?$/', $line, $m) && file_exists($tzfile = SRA_TIME_ZONE_ZONEINFO_PATH . '/' . $m[4])) {
$tzcache[$m[4]] = $m[1];
$countries[$m[1]] = TRUE;
}
}
// add gmt, utc and country specific time zones
$addlTimeZones = array(SRA_TIME_ZONE_ZONEINFO_PATH . '/' . SRA_TIME_ZONE_GMT, SRA_TIME_ZONE_ZONEINFO_PATH . '/' . SRA_TIME_ZONE_UTC);
foreach(array_keys($countries) as $temp) {
if (is_dir($cdir = SRA_TIME_ZONE_ZONEINFO_PATH . '/' . $temp)) {
foreach(SRA_File::getFileList($cdir) as $tzfile) {
$addlTimeZones[] = $tzfile;
}
}
}
foreach($addlTimeZones as $tzfile) {
if (!isset($tzcache[$tzfile])) {
$tzcache[str_replace(SRA_TIME_ZONE_ZONEINFO_PATH . '/', '', $tzfile)] = dirname($tzfile) == SRA_TIME_ZONE_ZONEINFO_PATH ? NULL : basename(dirname($tzfile));
}
}
ksort($tzcache);
// cache
SRA_File::write($ztCache, serialize($tzcache));
SRA_File::chmod($ztCache, 0666);
}
if (!$tzcache && file_exists($ztCache)) {
$tzcache = unserialize(SRA_File::toString($ztCache));
}
$timezones = $country ? array() : array_keys($tzcache);
// filter for a specific country
if ($country) {
$country = strtoupper($country);
foreach(array_keys($tzcache) as $zone) {
if ($tzcache[$zone] === NULL || $country == $tzcache[$zone]) {
$timezones[] = $zone;
}
}
}
if ($hash) {
foreach(array_keys($timezones) as $key) {
$timezones[$timezones[$key]] = $timezones[$key];
unset($timezones[$key]);
}
}
$cachedTimeZones[$cacheKey] = $timezones;
return $timezones;
}
// }}}
// {{{ getCountries
/**
* returns a hash of countries for which timezone configurations exist. the
* key in this hash will be the country code and the value will be the country
* name
* @param boolean $clearCache set to TRUE for force clearing of the cache
* @access public
* @return hash
*/
function getCountries($clearCache=FALSE) {
static $tzCountries = array();
if (!$tzCountries || $clearCache) {
$cacheFile = SRA_Controller::getSysTmpDir() . '/' . SRA_TIME_ZONE_ZONE_CACHE_PREFIX . 'countries';
if ($clearCache || !file_exists($cacheFile) || SRA_File::compareMTimes($cacheFile, SRA_TIME_ZONE_ZONETAB_PATH) != 1) {
$resources =& SRA_ResourceBundle::getBundle('iso3166');
foreach(file(SRA_TIME_ZONE_ZONETAB_PATH) as $line) {
if (preg_match('/^([A-Z]{2})?/', $line, $m)) {
if (trim($m[1])) $countries[$m[1]] = TRUE;
}
}
foreach(array_keys($countries) as $country) {
$tzCountries[$country] = $resources->getString($country);
}
asort($tzCountries);
$tzCountries =& SRA_Util::arrayMoveToTop($tzCountries, strtoupper(SRA_Controller::getAppDefaultCountry()));
SRA_File::write($cacheFile, serialize($tzCountries));
SRA_File::chmod($cacheFile, 0666);
}
if (!$tzCountries && file_exists($cacheFile)) {
$tzCountries = unserialize(SRA_File::toString($cacheFile));
}
}
return $tzCountries;
}
// }}}
// {{{ getSystemTimeZone
/**
* returns a reference to a object representing the timezone currently in use
* by the operating system (/etc/localtime)
* @access public
* @return SRA_TimeZone
*/
function &getSystemTimeZone() {
// if /etc/localtime is a link, just see what it is linked to
if (is_link(SRA_TIME_ZONE_SYSTEM) && ($link = readlink(SRA_TIME_ZONE_SYSTEM)) && SRA_TimeZone::isValid($tz =& SRA_TimeZone::getTimeZone(str_replace(SRA_TIME_ZONE_ZONEINFO_PATH . '/', '', $link)))) {
return $tz;
}
// otherwise check in /etc/sysconfig/clock
else if (is_array($clock = SRA_File::propertiesFileToArray(SRA_TIME_ZONE_CLOCK_CONFIG, 0)) && (isset($clock['ZONE']) || isset($clock['zone'])) && SRA_TimeZone::isValid($tz =& SRA_TimeZone::getTimeZone(SRA_Util::stripQuotes(isset($clock['ZONE']) ? $clock['ZONE'] : $clock['zone'])))) {
return $tz;
}
// last resort: compare filesizes of /etc/localtime and all of the timezone files in /usr/share/zoneinfo
else {
$size = filesize(SRA_TIME_ZONE_SYSTEM);
foreach(file(SRA_TIME_ZONE_ZONETAB_PATH) as $line) {
if (preg_match('/^([A-Z]{2})[\s]+([\+\-][0-9]+)([\+\-][0-9]+)[\s]+([\S]+)([\s]+[\S][\s\S]*)?$/', $line, $m) && file_exists($tzfile = SRA_TIME_ZONE_ZONEINFO_PATH . '/' . $m[4])) {
if (filesize($tzfile) == $size) {
return SRA_TimeZone::getTimeZone($m[4]);
}
}
}
}
}
// }}}
// {{{ getTimeZone
/**
* static singleton method used to obtain a reference to a time zone object.
* if $id is not a valid time zone identifier, NULL will be returned. if $id
* is not specified, the app time zone will be returned
* @param string $id the unique time zone name identifying which one should
* be returned. if not specified, the application time zone will be returned
* @param boolean $clearCache whether or not to clear the time zone cache if
* it exists
* @access public
* @return SRA_TimeZone
*/
function &getTimeZone($id=NULL, $clearCache=FALSE) {
static $_cachedTimeZones = array();
if (!$id) {
$conf =& SRA_Controller::getAppConf();
$id = isset($conf['time-zone']) ? $conf['time-zone'] : NULL;
}
if (!$id) {
$conf =& SRA_Controller::getSysConf();
$id = isset($conf['time-zone']) ? $conf['time-zone'] : NULL;
}
if (!$id) { return SRA_TimeZone::getSystemTimeZone(); }
$tz = NULL;
if (file_exists($tzfile = SRA_TIME_ZONE_ZONEINFO_PATH . '/' . $id)) {
if (!$clearCache && isset($_cachedTimeZones[$id])) {
$tz =& $_cachedTimeZones[$id];
}
else {
$tzCache = SRA_Controller::getSysTmpDir() . '/' . SRA_TIME_ZONE_ZONE_CACHE_PREFIX . str_replace('/', '_', $id);
if ($clearCache || !file_exists($tzCache) || SRA_File::compareMTimes($tzCache, $tzfile) != 1) {
$tzattrs = array('id' => $id, 'zoneFile' => $tzfile);
if (preg_match('/^([A-Z]{2})[\s]+([\+\-][0-9]+)([\+\-][0-9]+)[\s]+([\S]+)([\s]+[\S][\s\S]*)?$/', shell_exec(SRA_File::findInPath('grep') . " '" . $id . "' " . SRA_TIME_ZONE_ZONETAB_PATH), $m)) {
$tzattrs['country'] = $m[1];
$tzattrs['latitude'] = $m[2];
$tzattrs['longitude'] = $m[3];
$tzattrs['comment'] = isset($m[5]) ? trim($m[5]) : NULL;
}
else {
$tzattrs['country'] = dirname($tzfile) == SRA_TIME_ZONE_ZONEINFO_PATH ? NULL : basename(dirname($tzfile));
}
// generate and cache time zone ranges
if (exec(SRA_File::findInPath('zdump', SRA_TIME_ZONE_ZDUMP_PATH) . ' -v ' . $tzfile, $tmp)) {
$ranges = array();
$idx = -1;
$monthCache = array();
foreach($tmp as $range) {
if (preg_match('/=[\s]*[\S]{3}[\s]+([\S]{3})[\s]+([0-9]+)[\s]+([0-9]{2}):([0-9]{2}):([0-9]{2})[\s]+([0-9]{4})[\s]+([\S]+)[\s]+isdst[\s]*=[\s]*([01])[\s]+gmtoff[\s]*=[\s]*(\-?[0-9]+)/i', $range, $m)) {
if (!isset($monthCache[$m[1]])) $monthCache[$m[1]] = SRA_GregorianDate::getMonthFromStr($m[1]);
$stamp = ($m[6] . (strlen($monthCache[$m[1]]) == 1 ? '0' : '') . $monthCache[$m[1]] . (strlen($m[2]) == 1 ? '0' : '') . $m[2] . $m[3] . $m[4] . $m[5])*1;
$dstRange = $m[8] ? TRUE : FALSE;
$offset = $m[9]*1;
if ($dstRange && !$dst) $dst = $dstRange;
if ($dstRange) $abbrDst = $m[7];
if (!$dstRange) $abbr = $m[7];
if ($idx == -1 || $dstRange != $ranges[$idx]['dst'] || $offset != $ranges[$idx]['offset']) {
$ranges[++$idx] = array('start' => $stamp, 'end' => $stamp, 'dst' => $dstRange, 'offset' => $offset);
}
else {
$ranges[$idx]['end'] = $stamp;
}
}
}
// cache
$tzattrs['dst'] = $dst;
$tzattrs['abbr'] = $abbr;
$tzattrs['abbrDst'] = $abbrDst ? $abbrDst : $abbr;
$rangeCacheFile = SRA_TimeZone::_getRangesCacheFile($id);
SRA_File::write($rangeCacheFile, $buffer = '<?php ' . SRA_Util::bufferArray($ranges, 'this->_ranges') . ' ?>');
SRA_File::chmod($rangeCacheFile, 0666);
}
else {
$msg = 'SRA_TimeZone::getTimeZone: Error - Unable to zdump timezone ' . $id;
SRA_Error::logError($msg, __FILE__, __LINE__);
}
// cache time zone
SRA_File::write($tzCache, serialize($tzattrs));
}
$tz = new SRA_TimeZone($tzattrs ? $tzattrs : unserialize(SRA_File::toString($tzCache)));
$_cachedTimeZones[$id] =& $tz;
}
}
return $tz;
}
// }}}
// {{{ getTimeZoneByOffset
/**
* returns the timezone that utilizes the GMT $offset specified
* @param int $offset the offset to look for
* @param boolean $dst whether or not this is a dst offset (default is FALSE)
* @param int $year the year to consider (if not specified, the current year
* will be assumed)
* @access public
* @return SRA_TimeZone
*/
function &getTimeZoneByOffset($offset, $dst=FALSE, $year=NULL) {
static $_offsetTimeZones;
$key = $offset . ($dst ? '_dst' : '');
if (!$_offsetTimeZones) {
$_offsetTimeZones = array();
foreach(array_keys($allTimeZones =& SRA_TimeZone::getAllTimeZones()) as $i) {
if (($ofs = $allTimeZones[$i]->getGmtOffsetForYear($year) . '') && !isset($_offsetTimeZones[$ofs])) { $_offsetTimeZones[$ofs] =& $allTimeZones[$i]; }
if (($ofs = $allTimeZones[$i]->getGmtOffsetForYear($year, TRUE)) && !isset($_offsetTimeZones[$ofs])) { $_offsetTimeZones[$ofs . '_dst'] =& $allTimeZones[$i]; }
}
}
return isset($_offsetTimeZones[$key]) ? $_offsetTimeZones[$key] : ($nl = NULL);
}
// }}}
// {{{ getTzEnv
/**
* returns the a reference to an SRA_TimeZone instance representing the
* current timezone environment setting
* @param boolean $id when TRUE the id of the timezone will be returned
* instead of a reference to the SRA_TimeZone instance
* @access public
* @return SRA_TimeZone
*/
function &getTzEnv($id=FALSE) {
if (!getenv('TZ') || !SRA_TimeZone::isValid($tz =& SRA_TimeZone::getTimeZone(getenv('TZ')))) {
$tz =& SRA_TimeZone::getSystemTimeZone();
}
return $id ? $tz->getId() : $tz;
}
// }}}
// {{{ isValid
/**
* static method that returns true if $object is a SRA_TimeZone instance
* @param object $object the object to evaluate
* @access public
* @return boolean
*/
function isValid( &$object ) {
return (is_object($object) && (!isset($object->err) || !SRA_Error::isError($object->err)) && strtolower(get_class($object)) == 'sra_timezone');
}
// }}}
// {{{ setSystemTimeZone
/**
* sets the system timezone to $tz. this method can ONLY be invoked by the
* root user since root permissions are required in order to make this change
* returns TRUE on success, FALSE otherwise
* @param mixed $tz the new timezone to set. either the name or an actual
* SRA_TimeZone reference
* @access public
* @return boolean
*/
function setSystemTimeZone(&$tz) {
$ret = FALSE;
if (is_string($tz)) { $tz =& SRA_TimeZone::getTimeZone($tz); }
if (SRA_TimeZone::isValid($tz) && is_writable(dirname(SRA_TIME_ZONE_CLOCK_CONFIG))) {
SRA_File::unlink(SRA_TIME_ZONE_SYSTEM);
SRA_File::symlink($tz->getZoneFile(), SRA_TIME_ZONE_SYSTEM);
exec(SRA_File::findInPath('hwclock', SRA_TIME_ZONE_ZDUMP_PATH) . ' --systohc');
$clock = SRA_File::propertiesFileToArray(SRA_TIME_ZONE_CLOCK_CONFIG, 0);
$clock[isset($clock['ZONE']) ? 'ZONE' : 'zone'] = '"' . $tz->getId() . '"';
SRA_File::propertiesArrayToFile(SRA_TIME_ZONE_CLOCK_CONFIG, $clock);
$ret = TRUE;
}
return $ret;
}
// }}}
// {{{ setTzEnvVar
/**
* updates the timezone environment variable using the $tz specified. returns
* TRUE if the variable was changed, false otherwise
* @param mixed $tz the new timezone to set. either the name or an actual
* SRA_TimeZone reference
* @access public
* @return boolean
*/
function setTzEnvVar(&$tz) {
if (is_string($tz)) { $tz =& SRA_TimeZone::getTimeZone($tz); }
if (SRA_TimeZone::isValid($tz)) {
return putenv('TZ=' . $tz->getId());
}
else {
return FALSE;
}
}
// }}}
// {{{ validateCode
/**
* validates a time zone code
* @param string $code the time zone code to validate
* @access public
* @return boolean
*/
function validateCode($code) {
return SRA_TimeZone::isValid(SRA_TimeZone::getTimeZone($code)) ? TRUE : FALSE;
}
// }}}
}
// }}}
?>