Page Menu
Home
Phorge
Search
Configure Global Search
Log In
Files
F2527579
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Flag For Later
Award Token
Size
28 KB
Referenced Files
None
Subscribers
None
View Options
diff --git a/lib/kolab_sync_timezone_converter.php b/lib/kolab_sync_timezone_converter.php
index 8accbc1..ec83b56 100644
--- a/lib/kolab_sync_timezone_converter.php
+++ b/lib/kolab_sync_timezone_converter.php
@@ -1,613 +1,610 @@
<?php
/**
- * Tine 2.0
- *
- * @package ActiveSync
- * @license http://www.tine20.org/licenses/agpl-nonus.txt AGPL Version 1 (Non-US)
- * NOTE: According to sec. 8 of the AFFERO GENERAL PUBLIC LICENSE (AGPL),
- * Version 1, the distribution of the Tine 2.0 ActiveSync module in or to the
- * United States of America is excluded from the scope of this license.
- * @copyright Copyright (c) 2009 Metaways Infosystems GmbH (http://www.metaways.de)
- * @author Jonas Fischer <j.fischer@metaways.de>
- */
+ +--------------------------------------------------------------------------+
+ | Kolab Sync (ActiveSync for Kolab) |
+ | |
+ | Copyright (C) 2011-2017, Kolab Systems AG <contact@kolabsys.com> |
+ | Copyright (C) 2008-2012, Metaways Infosystems GmbH |
+ | |
+ | This program is free software: you can redistribute it and/or modify |
+ | it under the terms of the GNU Affero General Public License as published |
+ | by the Free Software Foundation, either version 3 of the License, or |
+ | (at your option) any later version. |
+ | |
+ | This program is distributed in the hope that it will be useful, |
+ | but WITHOUT ANY WARRANTY; without even the implied warranty of |
+ | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
+ | GNU Affero General Public License for more details. |
+ | |
+ | You should have received a copy of the GNU Affero General Public License |
+ | along with this program. If not, see <http://www.gnu.org/licenses/> |
+ +--------------------------------------------------------------------------+
+ | Author: Aleksander Machniak <machniak@kolabsys.com> |
+ | Author: Jonas Fischer <j.fischer@metaways.de> |
+ +--------------------------------------------------------------------------+
+*/
+/**
+ * Activesync timezone converter
+ */
class kolab_sync_timezone_converter
{
/**
* holds the instance of the singleton
*
* @var kolab_sync_timezone_onverter
*/
private static $_instance = NULL;
protected $_startDate = array();
/**
* If set then the timezone guessing results will be cached.
* This is strongly recommended for performance reasons.
*
* @var rcube_cache
*/
protected $cache = null;
/**
* array of offsets known by ActiceSync clients, but unknown by php
* @var array
*/
protected $_knownTimezones = array(
'0AIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==' => array(
'Pacific/Kwajalein' => 'MHT'
)
);
/**
* don't use the constructor. Use the singleton.
*
* @param $_logger
*/
private function __construct()
{
}
/**
* don't clone. Use the singleton.
- *
*/
private function __clone()
{
}
/**
* the singleton pattern
*
* @return kolab_sync_timezone_converter
*/
public static function getInstance()
{
if (self::$_instance === NULL) {
self::$_instance = new kolab_sync_timezone_converter();
}
return self::$_instance;
}
/**
- * Returns an array of timezones that match to the {@param $_offsets}
+ * Returns a list of timezones that match to the {@param $_offsets}
*
* If {@see $_expectedTimezone} is set then the method will terminate as soon
* as the expected timezone has matched and the expected timezone will be the
* first entry to the returned array.
*
* @param string|array $_offsets
*
* @return array
*/
public function getListOfTimezones($_offsets)
{
if (is_string($_offsets) && isset($this->_knownTimezones[$_offsets])) {
$timezones = $this->_knownTimezones[$_offsets];
}
else {
if (is_string($_offsets)) {
// unpack timezone info to array
$_offsets = $this->_unpackTimezoneInfo($_offsets);
}
if (!$this->_validateOffsets($_offsets)) {
return array();
}
$this->_setDefaultStartDateIfEmpty($_offsets);
$cacheId = $this->_getCacheId('timezones', $_offsets);
$timezones = $this->_loadFromCache($cacheId);
if (!is_array($timezones)) {
$timezones = array();
foreach (DateTimeZone::listIdentifiers() as $timezoneIdentifier) {
$timezone = new DateTimeZone($timezoneIdentifier);
if (false !== ($matchingTransition = $this->_checkTimezone($timezone, $_offsets))) {
$timezones[$timezoneIdentifier] = $matchingTransition['abbr'];
}
}
$this->_saveInCache($timezones, $cacheId);
}
}
return $timezones;
}
/**
* Returns PHP timezone that matches to the {@param $_offsets}
*
* If {@see $_expectedTimezone} is set then the method will return this timezone if it matches.
*
* @param string|array $_offsets Activesync timezone definition
* @param string $_expectedTomezone Expected timezone name
*
* @return string Expected timezone name
*/
public function getTimezone($_offsets, $_expectedTimezone = null)
{
$timezones = $this->getListOfTimezones($_offsets);
if ($_expectedTimezone && isset($timezones[$_expectedTimezone])) {
return $_expectedTimezone;
}
else {
return key($timezones);
}
}
/**
- * Unpacks {@param $_packedTimezoneInfo} using {@see unpackTimezoneInfo} and then
- * calls {@see getTimezoneForOffsets} with the unpacked timezone info
+ * Return packed string for given {@param $_timezone}
*
- * @param String $_packedTimezoneInfo
- * @return String [timezone abbreviation e.g. CET, MST etc.]
+ * @param string $_timezone Timezone identifier
+ * @param string|int $_startDate Start date
*
- */
-// public function getTimezoneForPackedTimezoneInfo($_packedTimezoneInfo)
-// {
-// $offsets = $this->_unpackTimezoneInfo($_packedTimezoneInfo);
-// $matchingTimezones = $this->getTimezoneForOffsets($offsets);
-// $maxMatches = 0;
-// $matchingAbbr = null;
-// foreach ($matchingTimezones as $abbr => $timezones) {
-// if (count($timezones) > $maxMatches) {
-// $maxMatches = count($timezones);
-// $matchingAbbr = $abbr;
-// }
-// }
-// return $matchingAbbr;
-// }
-
- /**
- * Return packed string for given {@param $_timezone}
- * @param String $_timezone
- * @param String | int | null $_startDate
- * @return String
+ * @return string Packed timezone offsets
*/
public function encodeTimezone($_timezone, $_startDate = null)
{
foreach ($this->_knownTimezones as $packedString => $knownTimezone) {
if (array_key_exists($_timezone, $knownTimezone)) {
return $packedString;
}
}
$offsets = $this->getOffsetsForTimezone($_timezone, $_startDate);
+
return $this->_packTimezoneInfo($offsets);
}
/**
- * get offsets for given timezone
+ * Get offsets for given timezone
*
- * @param string $_timezone
- * @param $_startDate
- * @return array
+ * @param string $_timezone Timezone identifier
+ * @param string|int $_startDate Start date
+ *
+ * @return array Timezone offsets
*/
public function getOffsetsForTimezone($_timezone, $_startDate = null)
{
$this->_setStartDate($_startDate);
$cacheId = $this->_getCacheId('offsets', array($_timezone));
if (false === ($offsets = $this->_loadFromCache($cacheId))) {
$offsets = $this->_getOffsetsTemplate();
try {
$timezone = new DateTimeZone($_timezone);
}
catch (Exception $e) {
-// $this->_log(__METHOD__, __LINE__, ": could not instantiate timezone {$_timezone}: {$e->getMessage()}");
return null;
}
list($standardTransition, $daylightTransition) = $this->_getTransitionsForTimezoneAndYear($timezone, $this->_startDate['year']);
if ($standardTransition) {
$offsets['bias'] = $standardTransition['offset']/60*-1;
if ($daylightTransition) {
$offsets = $this->_generateOffsetsForTransition($offsets, $standardTransition, 'standard');
$offsets = $this->_generateOffsetsForTransition($offsets, $daylightTransition, 'daylight');
//@todo how do we get the standardBias (is usually 0)?
//$offsets['standardBias'] = ...
$offsets['daylightBias'] = ($daylightTransition['offset'] - $standardTransition['offset'])/60*-1;
+ $offsets['standardHour'] -= $offsets['daylightBias'] / 60;
+ $offsets['daylightHour'] += $offsets['daylightBias'] / 60;
}
}
$this->_saveInCache($offsets, $cacheId);
}
return $offsets;
}
/**
+ * Get offsets for timezone transition
*
+ * @param array $_offsets Timezone offsets
+ * @param array $_transition Timezone transition information
+ * @param string $_type 'standard' or 'daylight'
*
- * @param array $_offsets
- * @param array $_transition
- * @param String $_type
* @return array
*/
- protected function _generateOffsetsForTransition(Array $_offsets, Array $_transition, $_type)
+ protected function _generateOffsetsForTransition(array $_offsets, array $_transition, $_type)
{
$transitionDateParsed = new DateTime($_transition['time']);
if ($_transition['offset']) {
$transitionDateParsed->modify($_transition['offset'] . ' seconds');
}
- $transitionDateParsed->modify(($_type == 'daylight' ? '-1 hour' : '+1 hour'));
-
$_offsets[$_type . 'Month'] = (int) $transitionDateParsed->format('n');
$_offsets[$_type . 'DayOfWeek'] = (int) $transitionDateParsed->format('w');
$_offsets[$_type . 'Minute'] = (int) $transitionDateParsed->format('i');
$_offsets[$_type . 'Hour'] = (int) $transitionDateParsed->format('G');
for ($i=5; $i>0; $i--) {
if ($this->_isNthOcurrenceOfWeekdayInMonth($_transition['ts'], $i)) {
$_offsets[$_type . 'Day'] = $i;
break;
};
}
return $_offsets;
}
/**
* Test if the weekday of the given {@param $_timestamp} is the {@param $_occurence}th occurence of this weekday within its month.
*
* @param int $_timestamp
* @param int $_occurence [1 to 5, where 5 indicates the final occurrence during the month if that day of the week does not occur 5 times]
*
* @return bool
*/
protected function _isNthOcurrenceOfWeekdayInMonth($_timestamp, $_occurence)
{
if ($_occurence <= 1) {
return true;
}
$original = new DateTime('@'.$_timestamp);
$orig = $original->format('n');
if ($_occurence == 5) {
$modified = clone($original);
$modified->modify('1 week');
$mod = $modified->format('n');
// modified date is a next month
return $mod > $orig || ($mod == 1 && $orig == 12);
}
$modified = clone($original);
$modified->modify(sprintf('-%d weeks', $_occurence - 1));
$mod = $modified->format('n');
if ($mod != $orig) {
return false;
}
$modified = clone($original);
$modified->modify(sprintf('-%d weeks', $_occurence));
$mod = $modified->format('n');
// modified month is earlier than original
return $mod < $orig || ($mod == 12 && $orig == 1);
}
/**
* Check if the given {@param $_standardTransition} and {@param $_daylightTransition}
* match to the object property {@see $_offsets}
*
- * @param Array $standardTransition
- * @param Array $daylightTransition
+ * @param array $standardTransition
+ * @param array $daylightTransition
*
* @return bool
*/
protected function _checkTransition($_standardTransition, $_daylightTransition, $_offsets)
{
if (empty($_standardTransition) || empty($_offsets)) {
return false;
}
$standardOffset = ($_offsets['bias'] + $_offsets['standardBias']) * 60 * -1;
- //check each condition in a single if statement and break the chain when one condition is not met - for performance reasons
+ // check each condition in a single if statement and break the chain when one condition is not met - for performance reasons
if ($standardOffset == $_standardTransition['offset'] ) {
if (empty($_offsets['daylightMonth']) && (empty($_daylightTransition) || empty($_daylightTransition['isdst']))) {
- //No DST
+ // No DST
return true;
}
$daylightOffset = ($_offsets['bias'] + $_offsets['daylightBias']) * 60 * -1;
// the milestone is sending a positive value for daylightBias while it should send a negative value
$daylightOffsetMilestone = ($_offsets['bias'] + ($_offsets['daylightBias'] * -1) ) * 60 * -1;
if ($daylightOffset == $_daylightTransition['offset'] || $daylightOffsetMilestone == $_daylightTransition['offset']) {
$standardParsed = getdate($_standardTransition['ts']);
$daylightParsed = getdate($_daylightTransition['ts']);
- if ($standardParsed['mon'] == $_offsets['standardMonth'] &&
+ if ($standardParsed['mon'] == $_offsets['standardMonth'] &&
$daylightParsed['mon'] == $_offsets['daylightMonth'] &&
$standardParsed['wday'] == $_offsets['standardDayOfWeek'] &&
$daylightParsed['wday'] == $_offsets['daylightDayOfWeek']
) {
return $this->_isNthOcurrenceOfWeekdayInMonth($_daylightTransition['ts'], $_offsets['daylightDay']) &&
$this->_isNthOcurrenceOfWeekdayInMonth($_standardTransition['ts'], $_offsets['standardDay']);
}
}
}
return false;
}
/**
* decode timezone info from activesync
*
* @param string $_packedTimezoneInfo the packed timezone info
* @return array
*/
protected function _unpackTimezoneInfo($_packedTimezoneInfo)
{
$timezoneUnpackString = 'lbias/a64standardName/vstandardYear/vstandardMonth/vstandardDayOfWeek/vstandardDay/vstandardHour/vstandardMinute/vstandardSecond/vstandardMilliseconds/lstandardBias/a64daylightName/vdaylightYear/vdaylightMonth/vdaylightDayOfWeek/vdaylightDay/vdaylightHour/vdaylightMinute/vdaylightSecond/vdaylightMilliseconds/ldaylightBias';
$timezoneInfo = unpack($timezoneUnpackString, base64_decode($_packedTimezoneInfo));
return $timezoneInfo;
}
/**
* encode timezone info to activesync
*
* @param array $_timezoneInfo
* @return string
*/
protected function _packTimezoneInfo($_timezoneInfo)
{
if (!is_array($_timezoneInfo)) {
return null;
}
$packed = pack(
"la64vvvvvvvvla64vvvvvvvvl",
$_timezoneInfo['bias'],
$_timezoneInfo['standardName'],
$_timezoneInfo['standardYear'],
$_timezoneInfo['standardMonth'],
$_timezoneInfo['standardDayOfWeek'],
$_timezoneInfo['standardDay'],
$_timezoneInfo['standardHour'],
$_timezoneInfo['standardMinute'],
$_timezoneInfo['standardSecond'],
$_timezoneInfo['standardMilliseconds'],
$_timezoneInfo['standardBias'],
$_timezoneInfo['daylightName'],
$_timezoneInfo['daylightYear'],
$_timezoneInfo['daylightMonth'],
$_timezoneInfo['daylightDayOfWeek'],
$_timezoneInfo['daylightDay'],
$_timezoneInfo['daylightHour'],
$_timezoneInfo['daylightMinute'],
$_timezoneInfo['daylightSecond'],
$_timezoneInfo['daylightMilliseconds'],
$_timezoneInfo['daylightBias']
);
return base64_encode($packed);
}
/**
* Returns complete offsets array with all fields empty
*
* Used e.g. when reverse-generating ActiveSync Timezone Offset Information
* based on a given Timezone, {@see getOffsetsForTimezone}
*
* @return unknown_type
*/
protected function _getOffsetsTemplate()
{
return array(
'bias' => 0,
'standardName' => '',
'standardYear' => 0,
'standardMonth' => 0,
'standardDayOfWeek' => 0,
'standardDay' => 0,
'standardHour' => 0,
'standardMinute' => 0,
'standardSecond' => 0,
'standardMilliseconds' => 0,
'standardBias' => 0,
'daylightName' => '',
'daylightYear' => 0,
'daylightMonth' => 0,
'daylightDayOfWeek' => 0,
'daylightDay' => 0,
'daylightHour' => 0,
'daylightMinute' => 0,
'daylightSecond' => 0,
'daylightMilliseconds' => 0,
'daylightBias' => 0
);
}
/**
* Validate and set offsets
*
* @param array $value
*
* @return bool Validation result
*/
protected function _validateOffsets($value)
{
// validate $value
if ((!empty($value['standardMonth']) || !empty($value['standardDay']) || !empty($value['daylightMonth']) || !empty($value['daylightDay'])) &&
(empty($value['standardMonth']) || empty($value['standardDay']) || empty($value['daylightMonth']) || empty($value['daylightDay']))
) {
// It is not possible not set standard offsets without setting daylight offsets and vice versa
return false;
}
return true;
}
/**
* Parse and set object property {@see $_startDate}
*
- * @param String | int $_startDate
+ * @param string|int $_startDate
* @return void
*/
protected function _setStartDate($_startDate)
{
if (empty($_startDate)) {
$this->_setDefaultStartDateIfEmpty();
return;
}
$startDateParsed = array();
if (is_string($_startDate)) {
$startDateParsed['string'] = $_startDate;
$startDateParsed['ts'] = strtotime($_startDate);
}
else if (is_int($_startDate)) {
$startDateParsed['ts'] = $_startDate;
$startDateParsed['string'] = strftime('%F', $_startDate);
}
else {
$this->_setDefaultStartDateIfEmpty();
return;
}
$startDateParsed['object'] = new DateTime($startDateParsed['string']);
$startDateParsed = array_merge($startDateParsed, getdate($startDateParsed['ts']));
$this->_startDate = $startDateParsed;
}
/**
* Set default value for object property {@see $_startdate} if it is not set yet.
* Tries to guess the correct startDate depending on object property {@see $_offsets} and
* falls back to current date.
*
* @param array $_offsets [offsets may be avaluated for a given start year]
* @return void
*/
protected function _setDefaultStartDateIfEmpty($_offsets = null)
{
if (!empty($this->_startDate)) {
return;
}
if (!empty($_offsets['standardYear'])) {
$this->_setStartDate($_offsets['standardYear'].'-01-01');
}
else {
$this->_setStartDate(time());
}
}
/**
* Check if the given {@param $_timezone} matches the {@see $_offsets}
* and also evaluate the daylight saving time transitions for this timezone if necessary.
*
* @param DateTimeZone $timezone
* @param array $offsets
*
* @return array|bool
*/
protected function _checkTimezone(DateTimeZone $timezone, $offsets)
{
list($standardTransition, $daylightTransition) = $this->_getTransitionsForTimezoneAndYear($timezone, $this->_startDate['year']);
if ($this->_checkTransition($standardTransition, $daylightTransition, $offsets)) {
return $standardTransition;
}
return false;
}
/**
* Returns the standard and daylight transitions for the given {@param $_timezone}
* and {@param $_year}.
*
* @param DateTimeZone $_timezone
- * @param $_year
- * @return Array
+ * @param int $_year
+ *
+ * @return array
*/
protected function _getTransitionsForTimezoneAndYear(DateTimeZone $_timezone, $_year)
{
$standardTransition = null;
$daylightTransition = null;
$start = mktime(0, 0, 0, 12, 1, $_year - 1);
$end = mktime(24, 0, 0, 12, 31, $_year);
$transitions = $_timezone->getTransitions($start, $end);
if ($transitions === false) {
return array();
}
foreach ($transitions as $index => $transition) {
if (strftime('%Y', $transition['ts']) == $_year) {
if (isset($transitions[$index+1]) && strftime('%Y', $transitions[$index]['ts']) == strftime('%Y', $transitions[$index+1]['ts'])) {
$daylightTransition = $transition['isdst'] ? $transition : $transitions[$index+1];
$standardTransition = $transition['isdst'] ? $transitions[$index+1] : $transition;
}
else {
$daylightTransition = $transition['isdst'] ? $transition : null;
$standardTransition = $transition['isdst'] ? null : $transition;
}
break;
}
else if ($index == count($transitions) -1) {
$standardTransition = $transition;
}
}
return array($standardTransition, $daylightTransition);
}
protected function _getCacheId($_prefix, $_offsets)
{
return $_prefix . md5(serialize($_offsets));
}
protected function _loadFromCache($key)
{
if ($cache = $this->getCache) {
return $cache->get($key);
}
return false;
}
protected function _saveInCache($value, $key)
{
if ($cache = $this->getCache) {
$cache->set($key, $value);
}
}
/**
* Getter for the cache engine object
*/
protected function getCache()
{
if ($this->cache === null) {
$rcube = rcube::get_instance();
$cache = $rcube->get_cache_shared('activesync');
$this->cache = $cache ? $cache : false;
}
return $this->cache;
}
}
diff --git a/tests/timezone_converter.php b/tests/timezone_converter.php
index 539c155..0cfcd3a 100644
--- a/tests/timezone_converter.php
+++ b/tests/timezone_converter.php
@@ -1,77 +1,98 @@
<?php
class timezone_converter extends PHPUnit_Framework_TestCase
{
function setUp()
{
}
function test_list_timezones()
{
$converter = timezone_converter_test::getInstance();
$input = 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAoAAAAEAAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMAAAAFAAEAAAAAAAAAxP///w==';
- $output = $converter->getListOfTimezones($input, 'UTC');
+ $output = $converter->getListOfTimezones($input);
$this->assertTrue(is_array($output));
}
+ function test_get_timezone()
+ {
+ $converter = timezone_converter_test::getInstance();
+ $datetime = new DateTime('2017-01-01T12:00:00Z');
+
+ $offsets = $converter->getOffsetsForTimezone('UTC', $datetime);
+ $output = $converter->getTimezone($offsets, 'UTC');
+
+ $this->assertSame('UTC', $output);
+
+ $offsets = $converter->getOffsetsForTimezone('Europe/Warsaw', $datetime);
+ $output = $converter->getTimezone($offsets, 'Europe/Warsaw');
+
+ $this->assertSame('Europe/Warsaw', $output);
+
+ $offsets = $converter->getOffsetsForTimezone('America/Los_angeles', $datetime);
+ $output = $converter->getTimezone($offsets, 'America/Los_Angeles');
+
+ $this->assertSame('America/Los_Angeles', $output);
+ }
+
function test_get_offsets_for_timezone()
{
$converter = timezone_converter_test::getInstance();
$datetime = new DateTime('2017-01-01T12:00:00Z');
$output = $converter->getOffsetsForTimezone('UTC', $datetime);
$this->assertSame($output['bias'], 0);
$this->assertSame($output['standardBias'], 0);
$this->assertSame($output['daylightBias'], 0);
$this->assertSame($output['standardMonth'], 0);
$this->assertSame($output['daylightMonth'], 0);
$output = $converter->getOffsetsForTimezone('Europe/Warsaw', $datetime);
$this->assertSame($output['standardBias'], 0);
$this->assertSame($output['standardMonth'], 10);
$this->assertSame($output['standardDay'], 5);
$this->assertSame($output['standardHour'], 3);
$this->assertSame($output['daylightBias'], -60);
$this->assertSame($output['daylightMonth'], 3);
$this->assertSame($output['daylightDay'], 5);
$this->assertSame($output['daylightHour'], 2);
$output = $converter->getOffsetsForTimezone('America/Los_Angeles', $datetime);
$this->assertSame($output['bias'], 480);
$this->assertSame($output['standardBias'], 0);
$this->assertSame($output['standardMonth'], 11);
$this->assertSame($output['standardDay'], 1);
$this->assertSame($output['standardHour'], 2);
$this->assertSame($output['daylightBias'], -60);
$this->assertSame($output['daylightMonth'], 3);
$this->assertSame($output['daylightDay'], 2);
$this->assertSame($output['daylightHour'], 2);
$output = $converter->getOffsetsForTimezone('Atlantic/Azores', $datetime);
$this->assertSame($output['bias'], 60);
$this->assertSame($output['standardBias'], 0);
$this->assertSame($output['standardMonth'], 10);
$this->assertSame($output['standardDay'], 5);
$this->assertSame($output['standardHour'], 1);
$this->assertSame($output['daylightBias'], -60);
$this->assertSame($output['daylightMonth'], 3);
$this->assertSame($output['daylightDay'], 5);
$this->assertSame($output['daylightHour'], 0);
}
}
class timezone_converter_test extends kolab_sync_timezone_converter
{
// disable cache
function getCache()
{
return null;
}
}
File Metadata
Details
Attached
Mime Type
text/x-diff
Expires
Sat, Jan 31, 1:31 AM (13 h, 37 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
426117
Default Alt Text
(28 KB)
Attached To
Mode
R4 syncroton
Attached
Detach File
Event Timeline
Log In to Comment