Skip to content

Commit

Permalink
Merge pull request #28 from slope-it/26-fix-absolute-date-with-timezone
Browse files Browse the repository at this point in the history
  • Loading branch information
asprega committed Nov 22, 2022
2 parents f0e4edf + 041a4d5 commit 3a56322
Show file tree
Hide file tree
Showing 2 changed files with 39 additions and 5 deletions.
26 changes: 21 additions & 5 deletions src/DateTimeMock/DateTimeMock.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,24 @@ public function __construct(?string $datetime = 'now', ?DateTimeZone $timezone =

parent::__construct($datetime, $timezone);

$this->setTimestamp(strtotime($datetime, ClockMock::getFrozenDateTime()->getTimestamp()));
$isDateTimeStringRelative = $this->isRelativeDateString($datetime);

if ($this->shouldUseMicrosecondsOfFrozenDate($datetime)) {
if ($timezone !== null && !$isDateTimeStringRelative) {
// When there's a timezone and the provided date is absolute, the timestamp must be calculated with that
// specific timezone in order to mimic behavior of the original \DateTime (which does not modify time).
$this->setTimestamp(
strtotime(
"$datetime {$timezone->getName()}",
ClockMock::getFrozenDateTime()->getTimestamp()
)
);
} else {
$this->setTimestamp(strtotime($datetime, ClockMock::getFrozenDateTime()->getTimestamp()));
}

// After some empirical tests, we've seen that microseconds are set to the current actual ones only when an
// absolute date or time is not provided.
if ($isDateTimeStringRelative) {
$this->setTime(
(int) $this->format('H'),
(int) $this->format('i'),
Expand All @@ -31,10 +46,11 @@ public function __construct(?string $datetime = 'now', ?DateTimeZone $timezone =
}
}

private function shouldUseMicrosecondsOfFrozenDate(string $datetime): bool
/**
* Returns whether the provided one is a relative date (e.g. "now", "yesterday", "tomorrow", etc...).
*/
private function isRelativeDateString(string $datetime): bool
{
// After some empirical tests, we've seen that microseconds are set to the current actual ones only when all of
// these variables are false (i.e. when an absolute date or time is not provided).
$parsedDate = date_parse($datetime);
return $parsedDate['year'] === false
&& $parsedDate['month'] === false
Expand Down
18 changes: 18 additions & 0 deletions tests/ClockMockTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,24 @@ public function test_DateTime_constructor_with_absolute_mocked_date()
$this->assertEquals($fakeNow, new \DateTime('now'));
}

/**
* @see https://github.com/slope-it/clock-mock/issues/26
*/
public function test_DateTime_constructor_with_absolute_date_and_timezone()
{
// The mocked date, either aboslute or relative, is irrelevant for this test. Having a mocked date is enough.
ClockMock::freeze(new \DateTime('now'));

$absoluteDateTimeWithTimezone = new \DateTime(
'1986-06-05 12:13:14',
$japanTimezone = new \DateTimeZone('Asia/Tokyo')
);

// Verification: when date is absolute and timezone is specified, the mocked clock should have no effect.
$this->assertEquals($japanTimezone, $absoluteDateTimeWithTimezone->getTimezone());
$this->assertSame('1986-06-05 12:13:14', $absoluteDateTimeWithTimezone->format('Y-m-d H:i:s'));
}

/**
* @see https://github.com/slope-it/clock-mock/issues/7
*/
Expand Down

0 comments on commit 3a56322

Please sign in to comment.