Skip to content

fix: parse fractional seconds in ChronosTime as a fraction of a second - #531

Merged
dereuromark merged 2 commits into
cakephp:3.xfrom
dualfroz:dualfroz/fix-chronostime-fractional-seconds
Sep 6, 2026
Merged

fix: parse fractional seconds in ChronosTime as a fraction of a second#531
dereuromark merged 2 commits into
cakephp:3.xfrom
dualfroz:dualfroz/fix-chronostime-fractional-seconds

Conversation

@dualfroz

@dualfroz dualfroz commented Sep 5, 2026

Copy link
Copy Markdown
Contributor

Problem

ChronosTime misinterprets a time string whose fractional-seconds part has fewer
than six digits. The fractional part is treated as a literal microsecond count
instead of a fraction of a second:

(new Cake\Chronos\ChronosTime('12:00:00.5'))->format('H:i:s.u');
// "12:00:00.000005"  (5 microseconds)
// expected "12:00:00.500000"  (half a second, matching DateTime)

(new DateTime('12:00:00.5'))->format('u'); // "500000"

So 12:00:00.5 becomes 5 microseconds rather than 500000, diverging from PHP's
own DateTime and from the round-trip used when constructing a ChronosTime
from a DateTimeInterface (which formats to six digits).

Root cause

src/ChronosTime.php, parseString():

$microseconds = (int)substr($matches[4] ?? '', 0, 6);

$matches[4] is the digit run after the decimal point. Passing it straight to
(int) reads "5" as 5, ignoring that these are the most-significant digits of
a fractional second. Only six-digit input (for example .500000) happened to
work, which is why the issue was not caught: every existing test uses a full
six-digit fraction (or a seven-digit one that is truncated to six).

The fix

Right-pad the (at most six) fractional digits to microseconds before converting:

$microseconds = (int)str_pad(substr($matches[4] ?? '', 0, 6), 6, '0', STR_PAD_RIGHT);
  • .5 -> 500000
  • .05 -> 50000
  • .500000 -> 500000 (unchanged)
  • .000005 -> 5 (unchanged)
  • .9999991-> truncated to 999999 (unchanged; existing behaviour preserved)

This matches how DateTime interprets fractional seconds.

Test

Added ChronosTimeTest::testConstructFromStringWithFractionalSeconds, asserting
that 12:00:00.5, 12:00:00.05 and 12:00:00.000005 produce
500000, 50000 and 5 microseconds respectively.

ChronosTime::parseString() passed the fractional-seconds digits straight to
(int), so a short fraction such as .5 was read as 5 microseconds instead of
500000 (half a second), diverging from DateTime. Right-pad the fractional digits
to microseconds before converting.
@dualfroz
dualfroz force-pushed the dualfroz/fix-chronostime-fractional-seconds branch from 540a12e to cdfce65 Compare September 5, 2026 23:20
@dereuromark
dereuromark merged commit 00c16f1 into cakephp:3.x Sep 6, 2026
8 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants