Providing support for timezone adjustments in date/time conversion methods
This commit is contained in:
parent
63c5cbc98e
commit
53a32ce7f7
|
@ -67,18 +67,26 @@ class PHPExcel_Shared_Date
|
||||||
* @private
|
* @private
|
||||||
* @var int
|
* @var int
|
||||||
*/
|
*/
|
||||||
private static $ExcelBaseDate = self::CALENDAR_WINDOWS_1900;
|
private static $_excelBaseDate = self::CALENDAR_WINDOWS_1900;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Default Timezone used for date/time conversions
|
||||||
|
*
|
||||||
|
* @private
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
private static $_timezone = 'UTC';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set the Excel calendar (Windows 1900 or Mac 1904)
|
* Set the Excel calendar (Windows 1900 or Mac 1904)
|
||||||
*
|
*
|
||||||
* @param integer $baseDate Excel base date
|
* @param integer $baseDate Excel base date (1900 or 1904)
|
||||||
* @return boolean Success or failure
|
* @return boolean Success or failure
|
||||||
*/
|
*/
|
||||||
public static function setExcelCalendar($baseDate) {
|
public static function setExcelCalendar($baseDate) {
|
||||||
if (($baseDate == self::CALENDAR_WINDOWS_1900) ||
|
if (($baseDate == self::CALENDAR_WINDOWS_1900) ||
|
||||||
($baseDate == self::CALENDAR_MAC_1904)) {
|
($baseDate == self::CALENDAR_MAC_1904)) {
|
||||||
self::$ExcelBaseDate = $baseDate;
|
self::$_excelBaseDate = $baseDate;
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
return FALSE;
|
return FALSE;
|
||||||
|
@ -88,33 +96,116 @@ class PHPExcel_Shared_Date
|
||||||
/**
|
/**
|
||||||
* Return the Excel calendar (Windows 1900 or Mac 1904)
|
* Return the Excel calendar (Windows 1900 or Mac 1904)
|
||||||
*
|
*
|
||||||
* @return integer $baseDate Excel base date
|
* @return integer Excel base date (1900 or 1904)
|
||||||
*/
|
*/
|
||||||
public static function getExcelCalendar() {
|
public static function getExcelCalendar() {
|
||||||
return self::$ExcelBaseDate;
|
return self::$_excelBaseDate;
|
||||||
} // function getExcelCalendar()
|
} // function getExcelCalendar()
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validate a Timezone value
|
||||||
|
*
|
||||||
|
* @param string $timezone Time zone (e.g. 'Europe/London')
|
||||||
|
* @return boolean Success or failure
|
||||||
|
*/
|
||||||
|
private static function _validateTimezone($timezone) {
|
||||||
|
if (in_array($timezone, DateTimeZone::listIdentifiers())) {
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the Default Timezone used for date/time conversions
|
||||||
|
*
|
||||||
|
* @param string $timezone Time zone (e.g. 'Europe/London')
|
||||||
|
* @return boolean Success or failure
|
||||||
|
*/
|
||||||
|
public static function setTimezone($timezone) {
|
||||||
|
if (self::_validateTimezone($timezone)) {
|
||||||
|
self::$_timezone = $timezone;
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
return FALSE;
|
||||||
|
} // function setTimezone()
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the Default Timezone used for date/time conversions
|
||||||
|
*
|
||||||
|
* @return string Timezone (e.g. 'Europe/London')
|
||||||
|
*/
|
||||||
|
public static function getTimezone() {
|
||||||
|
return self::$_timezone;
|
||||||
|
} // function getTimezone()
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the Timezone offset used for date/time conversions to/from UST
|
||||||
|
* This requires both the timezone and the calculated date/time to allow for local DST
|
||||||
|
*
|
||||||
|
* @param string $timezone The timezone for finding the adjustment to UST
|
||||||
|
* @param integer $timestamp PHP date/time value
|
||||||
|
* @return integer Number of seconds for timezone adjustment
|
||||||
|
* @throws PHPExcel_Exception
|
||||||
|
*/
|
||||||
|
private static function _getTimezoneAdjustment($timezone, $timestamp) {
|
||||||
|
if ($timezone !== NULL) {
|
||||||
|
if (!self::_validateTimezone($timezone)) {
|
||||||
|
throw new PHPExcel_Exception("Invalid timezone " . $timezone);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$timezone = self::$_timezone;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($timezone == 'UST') {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
$objTimezone = new DateTimeZone($timezone);
|
||||||
|
if (version_compare(PHP_VERSION, '5.3.0') >= 0) {
|
||||||
|
$transitions = $objTimezone->getTransitions($timestamp,$timestamp);
|
||||||
|
} else {
|
||||||
|
$allTransitions = $objTimezone->getTransitions();
|
||||||
|
$transitions = array();
|
||||||
|
foreach($allTransitions as $key => $transition) {
|
||||||
|
if ($transition['ts'] > $timestamp) {
|
||||||
|
$transitions[] = ($key > 0) ? $allTransitions[$key - 1] : $transitions1[] = $transition;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (empty($transitions)) {
|
||||||
|
$transitions[] = end($allTransitions);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (count($transitions) > 0) ? $transitions[0]['offset'] : 0;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Convert a date from Excel to PHP
|
* Convert a date from Excel to PHP
|
||||||
*
|
*
|
||||||
* @param long $dateValue Excel date/time value
|
* @param long $dateValue Excel date/time value
|
||||||
|
* @param boolean $adjustToTimezone Flag indicating whether $dateValue should be treated as
|
||||||
|
* a UST timestamp, or adjusted to UST
|
||||||
|
* @param string $timezone The timezone for finding the adjustment from UST
|
||||||
* @return long PHP serialized date/time
|
* @return long PHP serialized date/time
|
||||||
*/
|
*/
|
||||||
public static function ExcelToPHP($dateValue = 0) {
|
public static function ExcelToPHP($dateValue = 0, $adjustToTimezone = FALSE, $timezone = NULL) {
|
||||||
if (self::$ExcelBaseDate == self::CALENDAR_WINDOWS_1900) {
|
if (self::$_excelBaseDate == self::CALENDAR_WINDOWS_1900) {
|
||||||
$myExcelBaseDate = 25569;
|
$my_excelBaseDate = 25569;
|
||||||
// Adjust for the spurious 29-Feb-1900 (Day 60)
|
// Adjust for the spurious 29-Feb-1900 (Day 60)
|
||||||
if ($dateValue < 60) {
|
if ($dateValue < 60) {
|
||||||
--$myExcelBaseDate;
|
--$my_excelBaseDate;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
$myExcelBaseDate = 24107;
|
$my_excelBaseDate = 24107;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Perform conversion
|
// Perform conversion
|
||||||
if ($dateValue >= 1) {
|
if ($dateValue >= 1) {
|
||||||
$utcDays = $dateValue - $myExcelBaseDate;
|
$utcDays = $dateValue - $my_excelBaseDate;
|
||||||
$returnValue = round($utcDays * 86400);
|
$returnValue = round($utcDays * 86400);
|
||||||
if (($returnValue <= PHP_INT_MAX) && ($returnValue >= -PHP_INT_MAX)) {
|
if (($returnValue <= PHP_INT_MAX) && ($returnValue >= -PHP_INT_MAX)) {
|
||||||
$returnValue = (integer) $returnValue;
|
$returnValue = (integer) $returnValue;
|
||||||
|
@ -126,8 +217,10 @@ class PHPExcel_Shared_Date
|
||||||
$returnValue = (integer) gmmktime($hours, $mins, $secs);
|
$returnValue = (integer) gmmktime($hours, $mins, $secs);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$timezoneAdjustment = ($adjustToTimezone) ? self::_getTimezoneAdjustment($timezone, $returnValue) : 0;
|
||||||
|
|
||||||
// Return
|
// Return
|
||||||
return $returnValue;
|
return $returnValue + $timezoneAdjustment;
|
||||||
} // function ExcelToPHP()
|
} // function ExcelToPHP()
|
||||||
|
|
||||||
|
|
||||||
|
@ -159,7 +252,7 @@ class PHPExcel_Shared_Date
|
||||||
* @return mixed Excel date/time value
|
* @return mixed Excel date/time value
|
||||||
* or boolean FALSE on failure
|
* or boolean FALSE on failure
|
||||||
*/
|
*/
|
||||||
public static function PHPToExcel($dateValue = 0) {
|
public static function PHPToExcel($dateValue = 0, $adjustToTimezone = FALSE, $timezone = NULL) {
|
||||||
$saveTimeZone = date_default_timezone_get();
|
$saveTimeZone = date_default_timezone_get();
|
||||||
date_default_timezone_set('UTC');
|
date_default_timezone_set('UTC');
|
||||||
$retValue = FALSE;
|
$retValue = FALSE;
|
||||||
|
@ -190,16 +283,16 @@ class PHPExcel_Shared_Date
|
||||||
* @return long Excel date/time value
|
* @return long Excel date/time value
|
||||||
*/
|
*/
|
||||||
public static function FormattedPHPToExcel($year, $month, $day, $hours=0, $minutes=0, $seconds=0) {
|
public static function FormattedPHPToExcel($year, $month, $day, $hours=0, $minutes=0, $seconds=0) {
|
||||||
if (self::$ExcelBaseDate == self::CALENDAR_WINDOWS_1900) {
|
if (self::$_excelBaseDate == self::CALENDAR_WINDOWS_1900) {
|
||||||
//
|
//
|
||||||
// Fudge factor for the erroneous fact that the year 1900 is treated as a Leap Year in MS Excel
|
// Fudge factor for the erroneous fact that the year 1900 is treated as a Leap Year in MS Excel
|
||||||
// This affects every date following 28th February 1900
|
// This affects every date following 28th February 1900
|
||||||
//
|
//
|
||||||
$excel1900isLeapYear = TRUE;
|
$excel1900isLeapYear = TRUE;
|
||||||
if (($year == 1900) && ($month <= 2)) { $excel1900isLeapYear = FALSE; }
|
if (($year == 1900) && ($month <= 2)) { $excel1900isLeapYear = FALSE; }
|
||||||
$myExcelBaseDate = 2415020;
|
$my_excelBaseDate = 2415020;
|
||||||
} else {
|
} else {
|
||||||
$myExcelBaseDate = 2416481;
|
$my_excelBaseDate = 2416481;
|
||||||
$excel1900isLeapYear = FALSE;
|
$excel1900isLeapYear = FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -214,7 +307,7 @@ class PHPExcel_Shared_Date
|
||||||
// Calculate the Julian Date, then subtract the Excel base date (JD 2415020 = 31-Dec-1899 Giving Excel Date of 0)
|
// Calculate the Julian Date, then subtract the Excel base date (JD 2415020 = 31-Dec-1899 Giving Excel Date of 0)
|
||||||
$century = substr($year,0,2);
|
$century = substr($year,0,2);
|
||||||
$decade = substr($year,2,2);
|
$decade = substr($year,2,2);
|
||||||
$excelDate = floor((146097 * $century) / 4) + floor((1461 * $decade) / 4) + floor((153 * $month + 2) / 5) + $day + 1721119 - $myExcelBaseDate + $excel1900isLeapYear;
|
$excelDate = floor((146097 * $century) / 4) + floor((1461 * $decade) / 4) + floor((153 * $month + 2) / 5) + $day + 1721119 - $my_excelBaseDate + $excel1900isLeapYear;
|
||||||
|
|
||||||
$excelTime = (($hours * 3600) + ($minutes * 60) + $seconds) / 86400;
|
$excelTime = (($hours * 3600) + ($minutes * 60) + $seconds) / 86400;
|
||||||
|
|
||||||
|
|
|
@ -34,6 +34,30 @@ class DateTest extends PHPUnit_Framework_TestCase
|
||||||
$this->assertFalse($result);
|
$this->assertFalse($result);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function testSetTimezone()
|
||||||
|
{
|
||||||
|
$timezoneValues = array(
|
||||||
|
'Europe/Prague',
|
||||||
|
'Asia/Tokyo',
|
||||||
|
'America/Indiana/Indianapolis',
|
||||||
|
'Pacific/Honolulu',
|
||||||
|
'Atlantic/St_Helena',
|
||||||
|
);
|
||||||
|
|
||||||
|
foreach($timezoneValues as $timezoneValue) {
|
||||||
|
$result = call_user_func(array('PHPExcel_Shared_Date','setTimezone'),$timezoneValue);
|
||||||
|
$this->assertTrue($result);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testSetTimezoneWithInvalidValue()
|
||||||
|
{
|
||||||
|
$unsupportedTimezone = 'Etc/GMT+10';
|
||||||
|
$result = call_user_func(array('PHPExcel_Shared_Date','setTimezone'),$unsupportedTimezone);
|
||||||
|
$this->assertFalse($result);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @dataProvider providerDateTimeExcelToPHP1900
|
* @dataProvider providerDateTimeExcelToPHP1900
|
||||||
*/
|
*/
|
||||||
|
@ -161,4 +185,28 @@ class DateTest extends PHPUnit_Framework_TestCase
|
||||||
return new testDataFileIterator('rawTestData/Shared/DateTimeFormatCodes.data');
|
return new testDataFileIterator('rawTestData/Shared/DateTimeFormatCodes.data');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dataProvider providerDateTimeExcelToPHP1900Timezone
|
||||||
|
*/
|
||||||
|
public function testDateTimeExcelToPHP1900Timezone()
|
||||||
|
{
|
||||||
|
$result = call_user_func(
|
||||||
|
array('PHPExcel_Shared_Date','setExcelCalendar'),
|
||||||
|
PHPExcel_Shared_Date::CALENDAR_WINDOWS_1900
|
||||||
|
);
|
||||||
|
|
||||||
|
$args = func_get_args();
|
||||||
|
$expectedResult = array_pop($args);
|
||||||
|
if ($args[0] < 1) {
|
||||||
|
$expectedResult += gmmktime(0,0,0);
|
||||||
|
}
|
||||||
|
$result = call_user_func_array(array('PHPExcel_Shared_Date','ExcelToPHP'),$args);
|
||||||
|
$this->assertEquals($expectedResult, $result);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function providerDateTimeExcelToPHP1900Timezone()
|
||||||
|
{
|
||||||
|
return new testDataFileIterator('rawTestData/Shared/DateTimeExcelToPHP1900Timezone.data');
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,23 @@
|
||||||
|
#Excel DateTimeStamp Adjust Timezone Result Comments
|
||||||
|
22269, TRUE, 'America/New_York', -285138000 // 19-Dec-1960 00:00:00 UST
|
||||||
|
25569, TRUE, 'America/New_York', -18000 // PHP Base Date 01-Jan-1970 00:00:00 UST
|
||||||
|
30292, TRUE, 'America/New_York', 408049200 // 07-Dec-1982 00:00:00 UST
|
||||||
|
39611, TRUE, 'America/New_York', 1213214400 // 12-Jun-2008 00:00:00 UST
|
||||||
|
50424, TRUE, 'America/New_York', 2147454000 // PHP 32-bit Latest Date 19-Jan-2038 00:00:00 UST
|
||||||
|
22345.56789, TRUE, 'America/New_York', -278522534 // 18-May-1903 13:37:46 UST
|
||||||
|
22345.6789, TRUE, 'America/New_York', -278512943 // 18-Oct-1933 16:17:37 UST
|
||||||
|
0.5, TRUE, 'America/New_York', 28800 // 12:00:00 UST
|
||||||
|
0.75, TRUE, 'America/New_York', 50400 // 18:00.00 UST
|
||||||
|
0.12345, TRUE, 'America/New_York', -3734 // 02:57:46 UST
|
||||||
|
41215, TRUE, 'America/New_York', 1351800000 // 02-Nov-2012 00:00:00 UST
|
||||||
|
22269, TRUE, 'Pacific/Auckland', -285076800 // 19-Dec-1960 00:00:00 UST
|
||||||
|
25569, TRUE, 'Pacific/Auckland', 43200 // PHP Base Date 01-Jan-1970 00:00:00 UST
|
||||||
|
30292, TRUE, 'Pacific/Auckland', 408114000 // 07-Dec-1982 00:00:00 UST
|
||||||
|
39611, TRUE, 'Pacific/Auckland', 1213272000 // 12-Jun-2008 00:00:00 UST
|
||||||
|
50423.5, TRUE, 'Pacific/Auckland', 2147475600 // PHP 32-bit Latest Date 19-Jan-2038 00:00:00 UST
|
||||||
|
22345.56789, TRUE, 'Pacific/Auckland', -278461334 // 18-May-1903 13:37:46 UST
|
||||||
|
22345.6789, TRUE, 'Pacific/Auckland', -278451743 // 18-Oct-1933 16:17:37 UST
|
||||||
|
0.5, TRUE, 'Pacific/Auckland', 90000 // 12:00:00 UST
|
||||||
|
0.75, TRUE, 'Pacific/Auckland', 111600 // 18:00.00 UST
|
||||||
|
0.12345, TRUE, 'Pacific/Auckland', 57466 // 02:57:46 UST
|
||||||
|
41215, TRUE, 'Pacific/Auckland', 1351861200 // 02-Nov-2012 00:00:00 UST
|
|
@ -1,6 +1,6 @@
|
||||||
"PHPExcel", "8053"
|
"PHPExcel", "8053"
|
||||||
"Mark Baker", "877D"
|
"Mark Baker", "877D"
|
||||||
"I<3Am3l1a/*", "E3C8"
|
"!+&=()~§±æþ", "C0EA"
|
||||||
"μυστικό κωδικό πρόσβασης", "FFFF26DD"
|
"μυστικό κωδικό πρόσβασης", "FFFF26DD"
|
||||||
"গোপন পাসওয়ার্ড", "E858"
|
"গোপন পাসওয়ার্ড", "E858"
|
||||||
"Секретный пароль", "EA5F"
|
"Секретный пароль", "EA5F"
|
||||||
|
|
Loading…
Reference in New Issue