Skip to content

Commit

Permalink
Merge pull request #23 from Jeckel-Lab/feature/upgrade-timezone-handling
Browse files Browse the repository at this point in the history
Update timezone handling
  • Loading branch information
jeckel authored Aug 30, 2021
2 parents 5b407c2 + 83fbc88 commit 4d3b87a
Show file tree
Hide file tree
Showing 10 changed files with 505 additions and 118 deletions.
12 changes: 11 additions & 1 deletion src/Clock/FakedClock.php
Original file line number Diff line number Diff line change
Expand Up @@ -39,10 +39,12 @@ class FakedClock implements Clock
*/
public function __construct(DateTimeImmutable $initialDatetime, ?DateTimeZone $timezone = null)
{
$this->timezone = $timezone ?: new DateTimeZone(date_default_timezone_get());
$this->timezone = $timezone ?: $initialDatetime->getTimezone();

if ($initialDatetime->getTimezone()->getName() !== $this->timezone->getName()) {
// @codeCoverageIgnoreStart
$initialDatetime = $initialDatetime->setTimezone($this->timezone);
// @codeCoverageIgnoreEnd
}

$this->initialDatetime = $initialDatetime;
Expand Down Expand Up @@ -78,4 +80,12 @@ public function now(?DateTimeZone $timezone = null): DateTimeImmutable
}
// @codeCoverageIgnoreEnd
}

/**
* @return DateTimeZone
*/
public function getTimeZone(): DateTimeZone
{
return $this->timezone;
}
}
41 changes: 37 additions & 4 deletions src/Clock/FrozenClock.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@

use DateTimeImmutable;
use DateTimeZone;
use JeckelLab\Clock\Exception\InvalidFakeClockInitialValueException;
use JeckelLab\Clock\Exception\RuntimeException;
use JeckelLab\Contract\Infrastructure\System\Clock as ClockInterface;

/**
Expand All @@ -24,21 +26,44 @@ class FrozenClock implements ClockInterface
*/
protected $now;

/** @var DateTimeZone */
private $timezone;

/**
* FakeClock constructor.
* @param DateTimeImmutable $now
* @param DateTimeImmutable $initialDatetime
* @param DateTimeZone|null $timezone
*/
public function __construct(DateTimeImmutable $now)
public function __construct(DateTimeImmutable $initialDatetime, ?DateTimeZone $timezone = null)
{
$this->now = $now;
$this->timezone = $timezone ?: $initialDatetime->getTimezone();

if ($initialDatetime->getTimezone()->getName() !== $this->timezone->getName()) {
$fixedDateTime = $initialDatetime->setTimezone($this->timezone);
// @codeCoverageIgnoreStart
if (! $fixedDateTime instanceof DateTimeImmutable) {
throw new RuntimeException('Error setting timezone');
}
// @codeCoverageIgnoreEnd
$initialDatetime = $fixedDateTime;
}

$this->now = $initialDatetime;
}

/**
* @param DateTimeImmutable $now
*/
public function setClock(DateTimeImmutable $now): void
{
$this->now = $now;
$newNow = DateTimeImmutable::createFromFormat('U', $now->format('U'));
// @codeCoverageIgnoreStart
if (! $newNow instanceof DateTimeImmutable) {
throw new RuntimeException('Error creating new date');
}
// @codeCoverageIgnoreEnd

$this->now = $newNow->setTimezone($this->timezone);
}

/**
Expand All @@ -52,4 +77,12 @@ public function now(?DateTimeZone $timezone = null): DateTimeImmutable
}
return $this->now;
}

/**
* @return DateTimeZone
*/
public function getTimeZone(): DateTimeZone
{
return $this->timezone;
}
}
8 changes: 8 additions & 0 deletions src/Clock/RealClock.php
Original file line number Diff line number Diff line change
Expand Up @@ -54,4 +54,12 @@ public function now(?DateTimeZone $timezone = null): DateTimeImmutable
}
// @codeCoverageIgnoreEnd
}

/**
* @return DateTimeZone
*/
public function getTimeZone(): DateTimeZone
{
return $this->timezone;
}
}
10 changes: 5 additions & 5 deletions src/Factory/ClockFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -41,9 +41,9 @@ public static function getClock(array $config = []): ClockInterface
try {
switch ($config['mode'] ?? '') {
case 'frozen':
return new FrozenClock(self::getInitialTimeFromConfig($config));
return new FrozenClock(self::getInitialTimeFromConfig($config, $timezone));
case 'faked':
return new FakedClock(self::getInitialTimeFromConfig($config), $timezone);
return new FakedClock(self::getInitialTimeFromConfig($config, $timezone));
case 'real':
default:
return new RealClock($timezone);
Expand All @@ -61,18 +61,18 @@ public static function getClock(array $config = []): ClockInterface
* @return DateTimeImmutable
* @throws RuntimeException
*/
protected static function getInitialTimeFromConfig(array $config): DateTimeImmutable
protected static function getInitialTimeFromConfig(array $config, DateTimeZone $timeZone): DateTimeImmutable
{
try {
if (isset($config['fake_time_init'])) {
return new DateTimeImmutable((string)$config['fake_time_init']);
return new DateTimeImmutable((string) $config['fake_time_init'], $timeZone);
}
if (isset($config['fake_time_path'])) {
$filePath = (string) $config['fake_time_path'];
if (! is_readable($filePath)) {
throw new InvalidFakeClockInitialValueException('Impossible to read fake time file: ' . $filePath);
}
return new DateTimeImmutable(file_get_contents($filePath));
return new DateTimeImmutable(file_get_contents($filePath), $timeZone);
}
} catch (Exception $e) {
throw new InvalidFakeClockInitialValueException(
Expand Down
53 changes: 0 additions & 53 deletions tests/Clock/FakeClockTest.php

This file was deleted.

73 changes: 50 additions & 23 deletions tests/Clock/FakedClockTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,57 +9,84 @@

namespace Tests\JeckelLab\Clock\Clock;

use Cassandra\Date;
use DateTimeImmutable;
use DateTimeZone;
use JeckelLab\Clock\Clock\FakedClock;
use PHPUnit\Framework\TestCase;
use Tests\JeckelLab\Clock\ClockTestCase;

/**
* Class FakeRunningClockTest
* @package Tests\JeckelLab\Clock
*/
class FakedClockTest extends TestCase
class FakedClockTest extends ClockTestCase
{
public function testConstructorWithoutTimeZone(): void
{
$initTime = new DateTimeImmutable('2018-01-01 12:00:00');
$clock = new FakedClock($initTime);
$this->assertIsDefaultTimeZone($clock->getTimeZone());
}

public function testConstructorWithInitDateTimeZoned(): void
{
$initTimeZone = new DateTimeZone('Europe/Paris');
$initTime = new DateTimeImmutable('2018-01-01 12:00:00', $initTimeZone);
$clock = new FakedClock($initTime);
$this->assertEqualsTimeZone($initTimeZone, $clock->getTimeZone());
}

public function testConstructorWithTimeZone(): void
{
$initTimeZone = new DateTimeZone('Europe/Paris');
$initTime = new DateTimeImmutable('2018-01-01 12:00:00', $initTimeZone);
$clock = new FakedClock($initTime, $initTimeZone);
$this->assertEqualsTimeZone($initTimeZone, $clock->getTimeZone());
}

public function testWithPassedFakeTime(): void
{
$time = new DateTimeImmutable('2018-01-01 12:00:00');
$clock = new FakedClock($time);
sleep(1);
$newTime = $clock->now();
$this->assertGreaterThan($time, $newTime);
$this->assertEquals(1, $newTime->diff($time)->s);
$this->assertEquals('2018-01-01 12:00:01', $newTime->format('Y-m-d H:i:s'));
$now = $clock->now();
$this->assertGreaterThan($time, $now);
$this->assertEquals(1, $now->diff($time)->s);
$this->assertEquals('2018-01-01 12:00:01', $now->format('Y-m-d H:i:s'));
$this->assertHasDefaultTimeZone($now);
}

public function testWithFutureFakeTime(): void
{
$time = (new DateTimeImmutable('2040-01-01 12:00:00'));
$clock = new FakedClock($time);
sleep(1);
$newTime = $clock->now();
$this->assertGreaterThan($time, $newTime);
$this->assertEquals('2040-01-01 12:00:01', $newTime->format('Y-m-d H:i:s'));
$now = $clock->now();
$this->assertGreaterThan($time, $now);
$this->assertEquals('2040-01-01 12:00:01', $now->format('Y-m-d H:i:s'));
$this->assertHasDefaultTimeZone($now);
}

public function testTimeZone(): void
{
$clock = new FakedClock(
new DateTimeImmutable('2018-01-01 12:00:00'),
new DateTimeZone('GMT+2')
);
$initTimeZone = new DateTimeZone('Europe/Paris');
$initDateTime = new DateTimeImmutable('2018-01-01 12:00:00', $initTimeZone);
$clock = new FakedClock($initDateTime);

$timeWithDflTimezone = $clock->now();
$this->assertEquals('2018-01-01 14:00:00', $timeWithDflTimezone->format('Y-m-d H:i:s'));
$this->assertEquals('+02:00', $timeWithDflTimezone->getTimezone()->getName());
sleep(1);
$now = $clock->now();
$this->assertSameUniversalTime($initDateTime->add(new \DateInterval('PT1S')), $now);
$this->assertDateTimeHasTimeZone($initTimeZone, $now);

$timeWithUtcTimezone = $clock->now(new DateTimeZone('UTC'));
$this->assertEquals('2018-01-01 12:00:00', $timeWithUtcTimezone->format('Y-m-d H:i:s'));
$this->assertEquals('UTC', $timeWithUtcTimezone->getTimezone()->getName());
$expectedTimeZone = new DateTimeZone('America/New_York');
$nowWithDifferentTimezone = $clock->now($expectedTimeZone);
$this->assertSameUniversalTime(
$initDateTime->add(new \DateInterval('PT1S')),
$nowWithDifferentTimezone
);
$this->assertDateTimeHasTimeZone($expectedTimeZone, $nowWithDifferentTimezone);

// Different timezone, but same universal time
$this->assertEquals(
$timeWithDflTimezone->format('U'),
$timeWithUtcTimezone->format('U')
);
$this->assertSameUniversalTime($nowWithDifferentTimezone, $now);
}
}
Loading

0 comments on commit 4d3b87a

Please sign in to comment.