Skip to content

Fix createFromFormat to respect setTestNow for missing components#494

Open
dereuromark wants to merge 1 commit into3.xfrom
fix-createfromformat-testnow
Open

Fix createFromFormat to respect setTestNow for missing components#494
dereuromark wants to merge 1 commit into3.xfrom
fix-createfromformat-testnow

Conversation

@dereuromark
Copy link
Member

Summary

When setTestNow() is used for testing, createFromFormat() now uses values from the test time for any date/time components not present in the format string.

Previously, partial formats like createFromFormat('m-d', '10-05') would use the real system time for the year, making tests unreliable. Now it uses the testNow year.

The fix:

  • Parses the format string to detect which components (year, month, day, hour, minute, second, microsecond) are present
  • For missing components, uses values from testNow instead of real current time
  • Properly handles escaped characters in format strings
  • Respects ! and | modifiers which intentionally reset unspecified components to Unix epoch/zero

Example

Chronos::setTestNow(new Chronos('2020-12-01 00:00:00'));
$date = Chronos::createFromFormat('m-d', '10-05');
// Before: year from real system time (unpredictable in tests)
// After: 2020-10-05 00:00:00 (year from testNow)

Test plan

  • Added tests for various partial format scenarios
  • Added tests for escaped characters handling
  • Added tests for ! and | reset modifiers
  • All existing tests pass (909 tests)
  • Code style passes

Closes #452

When a test time is set via setTestNow(), createFromFormat() now uses
values from the test time for any date/time components not present in
the format string. This makes testing code that uses partial date formats
more reliable and predictable.

The fix:
- Detects which date/time components are in the format string
- For missing components, uses values from testNow instead of real time
- Respects '\!' and '|' modifiers which intentionally reset components

Closes #452
@dereuromark dereuromark added this to the 3.x milestone Jan 28, 2026
Comment on lines +696 to +701
// If the format includes '!' or '|', PHP resets unspecified components to Unix epoch or zero
// In that case, we should not override with testNow
$hasReset = in_array('!', $formatChars, true) || in_array('|', $formatChars, true);
if ($hasReset) {
return $dateTime;
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why are we adding new syntax to PHP's datetime formats?

Copy link
Member Author

@dereuromark dereuromark Feb 7, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We're not adding new syntax - ! and | are existing PHP DateTime format modifiers (see PHP docs).

The code is detecting their presence so we know to skip applying testNow values. When a user explicitly uses ! or |, they're telling PHP to reset unspecified components to Unix epoch, so we should respect that intent rather than substituting testNow values.

@dereuromark dereuromark requested a review from markstory February 7, 2026 13:55
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Chronos::createFromFormat using a date without time does not respect Chronos::setTestNow

2 participants