From 044e8f24af58fb4418ce052b3104624b34f4ffa3 Mon Sep 17 00:00:00 2001 From: bancer Date: Fri, 25 Jan 2019 12:05:44 +0100 Subject: [PATCH] Fix timezone conversion --- lib/Cake/Test/Case/Utility/CakeTimeTest.php | 73 +++++++++++++++++---- lib/Cake/Utility/CakeTime.php | 32 ++++++--- 2 files changed, 82 insertions(+), 23 deletions(-) diff --git a/lib/Cake/Test/Case/Utility/CakeTimeTest.php b/lib/Cake/Test/Case/Utility/CakeTimeTest.php index 91fb244f5..2d3c6b6e0 100644 --- a/lib/Cake/Test/Case/Utility/CakeTimeTest.php +++ b/lib/Cake/Test/Case/Utility/CakeTimeTest.php @@ -990,7 +990,19 @@ class CakeTimeTest extends CakeTestCase { $clientDateTime = new DateTime('2019-01-31 10:00:00', $clientTimeZone); // Convert to UTC. $actual = CakeTime::fromString($clientDateTime, 'UTC'); - $expected = 1548900000; // '2019-01-31 03:00:00' timestamp + $clientDateTime->setTimezone(new DateTimeZone('UTC')); + $expected = $clientDateTime->getTimestamp() + $clientDateTime->getOffset(); // 1548903600 + $this->assertEquals($expected, $actual); + $this->_restoreSystemTimezone(); + } + + public function testFromStringUTCtoCopenhagen() { + date_default_timezone_set('UTC'); // server timezone + $clientTimeZone = new DateTimeZone('UTC'); + $clientDateTime = new DateTime('2012-01-01 10:00:00', $clientTimeZone); + $actual = CakeTime::fromString($clientDateTime, 'Europe/Copenhagen'); + $clientDateTime->setTimezone(new DateTimeZone('Europe/Copenhagen')); + $expected = $clientDateTime->getTimestamp() + $clientDateTime->getOffset(); // 1325415600 $this->assertEquals($expected, $actual); $this->_restoreSystemTimezone(); } @@ -1007,6 +1019,25 @@ class CakeTimeTest extends CakeTestCase { $this->assertEquals($result, $date->format('U')); } + public function testConvertToBangkok() { + $serverTimeZoneName = 'Europe/Copenhagen'; + date_default_timezone_set($serverTimeZoneName); + + $serverTimeZone = new DateTimeZone($serverTimeZoneName); + $DateTime = new DateTime('2019-01-31 04:00:00', $serverTimeZone); + $serverTimestamp = $DateTime->getTimestamp() + $DateTime->getOffset(); // 1548907200 + + $clientTimeZoneName = 'Asia/Bangkok'; + $clientTimeZone = new DateTimeZone($clientTimeZoneName); + $DateTime->setTimezone($clientTimeZone); + $expected = $DateTime->getTimestamp() + $DateTime->getOffset(); // 1548928800 + + $actual = CakeTime::convert($serverTimestamp, $clientTimeZoneName); + $this->assertEquals($expected, $actual); + $this->_restoreSystemTimezone(); + } + + /** * test converting time specifiers using a time definition localfe file * @@ -1096,15 +1127,6 @@ class CakeTimeTest extends CakeTestCase { $this->assertEquals($expected, $result); } - public function testConvertTimezoneConversionToUTC() { - date_default_timezone_set('Europe/Copenhagen'); // server timezone - $serverTime = 1548903600; // '2019-01-31 04:00:00' timestamp - $actual = CakeTime::convert($serverTime, 'UTC'); - $expected = 1548900000; // '2019-01-31 03:00:00' timestamp - $this->assertEquals($expected, $actual); - $this->_restoreSystemTimezone(); - } - /** * test convert %e on Windows. * @@ -1173,10 +1195,22 @@ class CakeTimeTest extends CakeTestCase { $clientDateTime = new DateTime('2019-01-31 10:00:00', $clientTimeZone); // Convert to UTC. $actual = CakeTime::i18nFormat($clientDateTime, '%Y-%m-%d %H:%M:%S', false, 'UTC'); - $this->assertEquals('2019-01-31 03:00:00', $actual); + $clientDateTime->setTimezone(new DateTimeZone('UTC')); + $expected = $clientDateTime->format('Y-m-d H:i:s'); + $this->assertEquals($expected, $actual); $this->_restoreSystemTimezone(); } + public function testI18nFormatUTCtoCopenhagen() { + date_default_timezone_set('UTC'); + $clientTimeZone = new DateTimeZone('UTC'); + $clientDateTime = new DateTime('2012-01-01 10:00:00', $clientTimeZone); + $actual = CakeTime::i18nFormat($clientDateTime, '%Y-%m-%d %H:%M', false, 'Europe/Copenhagen'); + $clientDateTime->setTimezone(new DateTimeZone('Europe/Copenhagen')); + $expected = $clientDateTime->format('Y-m-d H:i'); + $this->assertEquals($expected, $actual); + } + /** * test new format() syntax which inverts first and second parameters * @@ -1245,7 +1279,7 @@ class CakeTimeTest extends CakeTestCase { * * @return void */ - public function testCorrectTimezoneConversion() { + public function testCorrectTimezoneConversionAsString() { date_default_timezone_set('UTC'); $date = '2012-01-01 10:00:00'; $converted = CakeTime::format($date, '%Y-%m-%d %H:%M', '', 'Europe/Copenhagen'); @@ -1254,13 +1288,26 @@ class CakeTimeTest extends CakeTestCase { $this->assertEquals($expected->format('Y-m-d H:i'), $converted); } + public function testCorrectTimezoneConversionAsObject() { + date_default_timezone_set('UTC'); + $clientTimeZone = new DateTimeZone('UTC'); + $date = '2012-01-01 10:00:00'; + $clientDateTime = new DateTime($date, $clientTimeZone); + $converted = CakeTime::format($clientDateTime, '%Y-%m-%d %H:%M', '', 'Europe/Copenhagen'); + $clientDateTime->setTimezone(new DateTimeZone('Europe/Copenhagen')); + $expected = $clientDateTime->format('Y-m-d H:i'); + $this->assertEquals($expected, $converted); + } + public function testFormatTimezoneConversionToUTC() { date_default_timezone_set('Europe/Copenhagen'); // server timezone $clientTimeZone = new DateTimeZone('Asia/Bangkok'); $clientDateTime = new DateTime('2019-01-31 10:00:00', $clientTimeZone); // Convert to UTC. $actual = CakeTime::format($clientDateTime, '%Y-%m-%d %H:%M:%S', false, 'UTC'); - $this->assertEquals('2019-01-31 03:00:00', $actual); + $clientDateTime->setTimezone(new DateTimeZone('UTC')); + $expected = $clientDateTime->format('Y-m-d H:i:s'); + $this->assertEquals($expected, $actual); $this->_restoreSystemTimezone(); } diff --git a/lib/Cake/Utility/CakeTime.php b/lib/Cake/Utility/CakeTime.php index 7383f34cb..14c78fce2 100644 --- a/lib/Cake/Utility/CakeTime.php +++ b/lib/Cake/Utility/CakeTime.php @@ -239,9 +239,9 @@ class CakeTime { /** * Converts given time (in server's time zone) to user's local time, given his/her timezone. * - * @param string $serverTime UNIX timestamp - * @param string|DateTimeZone $timezone User's timezone string or DateTimeZone object - * @return int UNIX timestamp + * @param integer $serverTime Server's timestamp. + * @param string|DateTimeZone $timezone User's timezone string or DateTimeZone object. + * @return int User's timezone timestamp. * @link https://book.cakephp.org/2.0/en/core-libraries/helpers/time.html#TimeHelper::convert */ public static function convert($serverTime, $timezone) { @@ -303,11 +303,11 @@ class CakeTime { } /** - * Returns a UNIX timestamp, given either a UNIX timestamp or a valid strtotime() date string. + * Returns a timestamp, given either a UNIX timestamp or a valid strtotime() date string. * * @param int|string|DateTime $dateString UNIX timestamp, strtotime() valid string or DateTime object * @param string|DateTimeZone $timezone Timezone string or DateTimeZone object - * @return string Parsed timestamp + * @return int|false Parsed given timezone timestamp. * @link https://book.cakephp.org/2.0/en/core-libraries/helpers/time.html#TimeHelper::fromString */ public static function fromString($dateString, $timezone = null) { @@ -327,7 +327,7 @@ class CakeTime { ) { $clone = clone $dateString; $clone->setTimezone(new DateTimeZone(date_default_timezone_get())); - $date = (int)$clone->format('U')/* + $clone->getOffset()*/; + $date = (int)$clone->format('U') + $clone->getOffset(); } elseif ($dateString instanceof DateTime) { $date = (int)$dateString->format('U'); } else { @@ -1055,17 +1055,29 @@ class CakeTime { * @link https://book.cakephp.org/2.0/en/core-libraries/helpers/time.html#TimeHelper::i18nFormat */ public static function i18nFormat($date, $format = null, $default = false, $timezone = null) { - $date = static::fromString($date, $timezone); - if ($date === false && $default !== false) { + $timestamp = static::fromString($date, $timezone); + if ($timestamp === false && $default !== false) { return $default; } - if ($date === false) { + if ($timestamp === false) { return ''; } if (empty($format)) { $format = '%x'; } - return static::_strftime(static::convertSpecifiers($format, $date), $date); + $serverTimeZone = date_default_timezone_get(); + if ( + !empty($timezone) && + $date instanceof DateTime && + $date->getTimezone()->getName() != $serverTimeZone + ) { + date_default_timezone_set($timezone); + } + $result = static::_strftime(static::convertSpecifiers($format, $timestamp), $timestamp); + if (!empty($serverTimeZone)) { + date_default_timezone_set($serverTimeZone); + } + return $result; } /**