From cf1942c50c3f9d25a07ab5cc85f4703e2243ed0e Mon Sep 17 00:00:00 2001 From: mscherer Date: Sat, 14 Mar 2026 17:36:44 +0100 Subject: [PATCH] Fix createFromFormat with Unix timestamp format 'U' The 3.3.2 fix for respecting testNow in createFromFormat incorrectly applied testNow values when using the Unix timestamp format 'U'. Since 'U' sets all date/time components from the Unix timestamp value, testNow should not be applied. This caused createFromFormat('U', '0') to return the current test time instead of the Unix epoch (1970-01-01 00:00:00). The fix adds 'U' to the list of format specifiers that should skip the testNow application, similar to '!' and '|'. --- src/Chronos.php | 6 ++++-- tests/TestCase/DateTime/CreateFromFormatTest.php | 16 ++++++++++++++++ 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/src/Chronos.php b/src/Chronos.php index ada7963..635c109 100644 --- a/src/Chronos.php +++ b/src/Chronos.php @@ -694,9 +694,11 @@ protected static function applyTestNowToMissingComponents( $hasMicro = (bool)array_intersect($formatChars, ['u', 'v']); // If the format includes '!' or '|', PHP resets unspecified components to Unix epoch or zero - // In that case, we should not override with testNow + // If 'U' is present, all components are set from the Unix timestamp + // In these cases, we should not override with testNow $hasReset = in_array('!', $formatChars, true) || in_array('|', $formatChars, true); - if ($hasReset) { + $hasUnixTimestamp = in_array('U', $formatChars, true); + if ($hasReset || $hasUnixTimestamp) { return $dateTime; } diff --git a/tests/TestCase/DateTime/CreateFromFormatTest.php b/tests/TestCase/DateTime/CreateFromFormatTest.php index 91318db..bdf826b 100644 --- a/tests/TestCase/DateTime/CreateFromFormatTest.php +++ b/tests/TestCase/DateTime/CreateFromFormatTest.php @@ -111,6 +111,22 @@ public function testCreateFromFormatWithTestNowMicroseconds() $this->assertSame(123456, $d->micro); } + public function testCreateFromFormatWithTestNowUnixTimestamp() + { + // Unix timestamp ('U' format) sets all components, should not use testNow + Chronos::setTestNow(new Chronos('2020-12-01 14:30:45')); + $d = Chronos::createFromFormat('U', '0'); + $this->assertDateTime($d, 1970, 1, 1, 0, 0, 0); + } + + public function testCreateFromFormatWithTestNowNegativeUnixTimestamp() + { + // Negative Unix timestamp should also not use testNow + Chronos::setTestNow(new Chronos('2020-12-01 14:30:45')); + $d = Chronos::createFromFormat('U', '-1000'); + $this->assertDateTime($d, 1969, 12, 31, 23, 43, 20); + } + public function testCreateFromFormatWithTimezoneString() { $d = Chronos::createFromFormat('Y-m-d H:i:s', '1975-05-21 22:32:11', 'Europe/London');