Skip to content

Right-pad sub-3-digit fractional seconds when parsing ISO dates - #2341

Open
raphyabak wants to merge 1 commit into
jquense:masterfrom
raphyabak:fix/iso-date-subsecond-padding
Open

raphyabak wants to merge 1 commit into
jquense:masterfrom
raphyabak:fix/iso-date-subsecond-padding

Conversation

@raphyabak

Copy link
Copy Markdown

Summary

Fixes #2339.

import { date } from 'yup';

date().cast('2020-01-01T00:00:00.5Z').toISOString();
// actual:   "2020-01-01T00:00:00.005Z"
// expected: "2020-01-01T00:00:00.500Z"   (matches new Date('2020-01-01T00:00:00.5Z'))

parseDateStruct() in src/util/parseIsoDate.ts truncates the fractional-seconds capture group to its first 3 characters and reads that directly as milliseconds:

toNumber(regexResult[7].substring(0, 3))

For a 3+ digit fraction this is a correct truncation (.123 → 123), but for anything shorter it's wrong, because the substring is read as a literal integer rather than a fraction: .5 → 5 instead of 500, .05 → 5 instead of 50, .12 → 12 instead of 120.

Fix

Right-pad the truncated fraction with zeros before parsing it as a number (.substring(0, 3).padEnd(3, '0')), matching the native ISO 8601 parser (new Date / Date.parse) that this file's own header says it enhances. This doesn't change behavior for 3+ digit fractions, which padEnd leaves untouched.

(This is distinct from #60, which fixed the leading-zero case for exactly-3-digit fractions like .012; that case was already correct and stays correct here.)

Test plan

  • Added a fractional seconds shorter than 3 digits block in test/util/parseIsoDate.ts covering .5, .05, and .12, each asserted against both the expected Date.UTC(...) value and Date.parse(...) directly.
  • Verified the regression: reverting the source change turns all 3 new tests from passing into failing with exactly the wrong values from the issue (5ms instead of 500ms, 5ms instead of 50ms, 12ms instead of 120ms).
  • Full suite passes: yarn vitest run — 873 passing, 2 pre-existing skipped.
  • yarn lint passes.

Fixes jquense#2339.

parseDateStruct() truncated the fractional-seconds capture group to its
first 3 characters and read that directly as milliseconds. For a
fraction with 3+ digits this is a correct truncation, but for a shorter
fraction it's wrong: ".5" (500ms) was read as the literal integer 5,
".05" (50ms) as 5, and ".12" (120ms) as 12.

Right-pad the truncated fraction with zeros before parsing it as a
number, so it matches the native ISO 8601 parser (`new Date` /
`Date.parse`), which parseIsoDate.ts's own header says it enhances.
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.

date() cast mis-parses sub-3-digit fractional seconds (.5Z -> 5ms instead of 500ms)

1 participant