# fix: parse fractional seconds in ChronosTime as a fraction of a second ## 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: ```php (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()`: ```php $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: ```php $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. ## Gate ``` composer install && vendor/bin/phpunit ``` Result: exit code `0` - `OK (981 tests, 2496 assertions)`. Counterfactual: reverting `parseString()` to `(int)substr(...)` makes the new test fail - `12:00:00.5` yields `12:00:00.000005` instead of `12:00:00.500000`.