diff --git a/src/Chronos.php b/src/Chronos.php index eadcbb1..8957388 100644 --- a/src/Chronos.php +++ b/src/Chronos.php @@ -668,6 +668,12 @@ public static function createFromTime( /** * Create an instance from a specific format * + * Unlike PHP's native DateTimeImmutable::createFromFormat(), this method + * automatically appends the `|` modifier if no reset modifier (`|` or `!`) + * is present. This ensures that unparsed components are reset to zero + * instead of being filled from the current time, providing more predictable + * and deterministic behavior. + * * @param string $format The date() compatible format string. * @param string $time The formatted date string to interpret. * @param \DateTimeZone|string|null $timezone The DateTimeZone object or timezone name the new instance should use. @@ -679,6 +685,12 @@ public static function createFromFormat( string $time, DateTimeZone|string|null $timezone = null, ): static { + // Auto-append | modifier if no reset modifier is present + // This ensures unparsed components are zero instead of current time + if (!str_contains($format, '|') && !str_contains($format, '!')) { + $format .= '|'; + } + if ($timezone !== null) { $dateTime = parent::createFromFormat($format, $time, $timezone ? static::safeCreateDateTimeZone($timezone) : null); } else { diff --git a/tests/TestCase/DateTime/CreateFromFormatTest.php b/tests/TestCase/DateTime/CreateFromFormatTest.php index f124679..a98d240 100644 --- a/tests/TestCase/DateTime/CreateFromFormatTest.php +++ b/tests/TestCase/DateTime/CreateFromFormatTest.php @@ -29,6 +29,49 @@ public function testCreateFromFormatReturnsInstance() $this->assertTrue($d instanceof Chronos); } + public function testCreateFromFormatMissingTimeIsZero() + { + // Missing time components should be zero, not current time + $d = Chronos::createFromFormat('Y-m-d', '2024-03-14'); + $this->assertDateTime($d, 2024, 3, 14, 0, 0, 0); + $this->assertSame(0, $d->micro); + } + + public function testCreateFromFormatMissingSecondsIsZero() + { + // Missing seconds should be zero + $d = Chronos::createFromFormat('Y-m-d H:i', '2024-03-14 12:30'); + $this->assertDateTime($d, 2024, 3, 14, 12, 30, 0); + } + + public function testCreateFromFormatMissingDateIsEpoch() + { + // Missing date components should be Unix epoch (1970-01-01) + $d = Chronos::createFromFormat('H:i:s', '12:30:45'); + $this->assertDateTime($d, 1970, 1, 1, 12, 30, 45); + } + + public function testCreateFromFormatMissingMicrosecondsIsZero() + { + // Missing microseconds should be zero + $d = Chronos::createFromFormat('Y-m-d H:i:s', '2024-03-14 12:30:45'); + $this->assertSame(0, $d->micro); + } + + public function testCreateFromFormatWithExplicitPipeModifier() + { + // Explicit | should still work + $d = Chronos::createFromFormat('Y-m-d|', '2024-03-14'); + $this->assertDateTime($d, 2024, 3, 14, 0, 0, 0); + } + + public function testCreateFromFormatWithExplicitBangModifier() + { + // Explicit ! should still work + $d = Chronos::createFromFormat('!Y-m-d', '2024-03-14'); + $this->assertDateTime($d, 2024, 3, 14, 0, 0, 0); + } + public function testCreateFromFormatWithTimezoneString() { $d = Chronos::createFromFormat('Y-m-d H:i:s', '1975-05-21 22:32:11', 'Europe/London'); @@ -49,6 +92,18 @@ public function testCreateFromFormatWithMillis() $this->assertSame(254687, $d->micro); } + public function testCreateFromFormatWithUnixTimestamp() + { + $d = Chronos::createFromFormat('U', '0'); + $this->assertDateTime($d, 1970, 1, 1, 0, 0, 0); + } + + public function testCreateFromFormatWithNegativeUnixTimestamp() + { + $d = Chronos::createFromFormat('U', '-1000'); + $this->assertDateTime($d, 1969, 12, 31, 23, 43, 20); + } + public function testCreateFromFormatInvalidFormat() { $parseException = null; @@ -65,9 +120,9 @@ public function testCreateFromFormatInvalidFormat() public function testCreateFromFormatDoesNotUseTestNow() { - // createFromFormat should not use testNow - it should behave like PHP's native method + // testNow should not affect createFromFormat - missing components are zero Chronos::setTestNow(new Chronos('2020-12-01 14:30:45')); - $d = Chronos::createFromFormat('Y-m-d H:i:s', '1975-05-21 22:32:11'); - $this->assertDateTime($d, 1975, 5, 21, 22, 32, 11); + $d = Chronos::createFromFormat('Y-m-d', '2024-03-14'); + $this->assertDateTime($d, 2024, 3, 14, 0, 0, 0); } }