From 89683faff3ac100d2ee52d072acc7bddb988475d Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Sun, 18 Sep 2016 18:44:44 +0200 Subject: [PATCH] Fixes microsecond timezone support, fixes #832 --- src/Monolog/DateTimeImmutable.php | 11 +++++-- tests/Monolog/LoggerTest.php | 50 +++++++++++++++++++++++++++++++ 2 files changed, 59 insertions(+), 2 deletions(-) diff --git a/src/Monolog/DateTimeImmutable.php b/src/Monolog/DateTimeImmutable.php index 6ca786c1..30ef8091 100644 --- a/src/Monolog/DateTimeImmutable.php +++ b/src/Monolog/DateTimeImmutable.php @@ -28,9 +28,16 @@ class DateTimeImmutable extends \DateTimeImmutable implements \JsonSerializable // Circumvent DateTimeImmutable::createFromFormat() which always returns \DateTimeImmutable instead of `static` // @link https://bugs.php.net/bug.php?id=60302 $timestamp = microtime(true); - $microseconds = sprintf("%06d", ($timestamp - floor($timestamp)) * 1000000); - $date = date('Y-m-d H:i:s.' . $microseconds, (int) $timestamp); + + // apply offset of the timezone as microtime() is always UTC + if ($timezone && $timezone->getName() !== 'UTC') { + $timestamp += (new \DateTime('now', $timezone))->getOffset(); + } + + $dt = self::createFromFormat('U.u', sprintf('%.6F', $timestamp)); + $date = $dt->format('Y-m-d H:i:s.u'); } + parent::__construct($date, $timezone); $this->useMicroseconds = $useMicroseconds; diff --git a/tests/Monolog/LoggerTest.php b/tests/Monolog/LoggerTest.php index 75c2dbd3..245660a9 100644 --- a/tests/Monolog/LoggerTest.php +++ b/tests/Monolog/LoggerTest.php @@ -494,6 +494,56 @@ class LoggerTest extends \PHPUnit_Framework_TestCase ); } + /** + * @covers Monolog\Logger::setTimezone + * @covers Monolog\DateTimeImmutable::__construct + */ + public function testTimezoneIsRespectedInUTC() + { + foreach ([true, false] as $microseconds) { + $logger = new Logger('foo'); + $logger->useMicrosecondTimestamps($microseconds); + $tz = new \DateTimeZone('America/New_York'); + $logger->setTimezone($tz); + $handler = new TestHandler; + $logger->pushHandler($handler); + $dt = new \DateTime('now', $tz); + $logger->info('test'); + list($record) = $handler->getRecords(); + + $this->assertEquals($tz, $record['datetime']->getTimezone()); + $this->assertEquals($dt->format('Y/m/d H:i'), $record['datetime']->format('Y/m/d H:i'), 'Time should match timezone with microseconds set to: '.var_export($microseconds, true)); + } + } + + /** + * @covers Monolog\Logger::setTimezone + * @covers Monolog\DateTimeImmutable::__construct + */ + public function testTimezoneIsRespectedInOtherTimezone() + { + date_default_timezone_set('CET'); + foreach ([true, false] as $microseconds) { + $logger = new Logger('foo'); + $logger->useMicrosecondTimestamps($microseconds); + $tz = new \DateTimeZone('America/New_York'); + $logger->setTimezone($tz); + $handler = new TestHandler; + $logger->pushHandler($handler); + $dt = new \DateTime('now', $tz); + $logger->info('test'); + list($record) = $handler->getRecords(); + + $this->assertEquals($tz, $record['datetime']->getTimezone()); + $this->assertEquals($dt->format('Y/m/d H:i'), $record['datetime']->format('Y/m/d H:i'), 'Time should match timezone with microseconds set to: '.var_export($microseconds, true)); + } + } + + public function tearDown() + { + date_default_timezone_set('UTC'); + } + /** * @dataProvider useMicrosecondTimestampsProvider * @covers Monolog\Logger::useMicrosecondTimestamps