Skip to content

CsvHelpers: validate MM/DD/YYYY dates in UTC#97

Open
arpitjain099 wants to merge 1 commit into
CMSgov:mainfrom
arpitjain099:chore/date-validation-utc
Open

CsvHelpers: validate MM/DD/YYYY dates in UTC#97
arpitjain099 wants to merge 1 commit into
CMSgov:mainfrom
arpitjain099:chore/date-validation-utc

Conversation

@arpitjain099

@arpitjain099 arpitjain099 commented Jun 27, 2026

Copy link
Copy Markdown

isValidDate in src/validators/CsvHelpers.ts checks the parsed date with getUTCFullYear() / getUTCMonth() / getUTCDate(). That's right for the ISO YYYY-MM-DD branch, where date-only strings parse as UTC, but wrong for the MM/DD/YYYY (and M/D/YYYY) branch: the slash form parses in the local time zone and then gets read back with UTC getters.

In any positive UTC offset (a contributor in Asia/Tokyo, or a CI runner not pinned to UTC), local midnight lands on the previous calendar day in UTC, so the getters report the day before, the equality check fails, and a valid last_updated_on like 05/01/2024 is flagged as InvalidDateError. The repo's own tests show it: TZ=UTC npx vitest run passes, but TZ=Asia/Tokyo npx vitest run fails the MM/DD/YYYY and M/D/YYYY last_updated_on cases for v2.0.0, v2.1.0, and v2.2.0. The suite already encodes the right behavior and only passes because CI runs in UTC.

The fix builds the date in UTC from the components already parsed out of the string instead of re-parsing the raw value:

const parsedDate = new Date(Date.UTC(expectedYear, expectedMonth, expectedDate));

The impossible-date guard (Feb 31, month 13) still works since Date.UTC normalizes the same way and the equality check rejects them. The ISO branch is left alone, and I fixed the now-wrong comment that claimed the slash form parsed as UTC.

No new tests needed; the existing suite covers it, it just had to run outside UTC. After the change TZ=Asia/Tokyo, TZ=UTC, and TZ=America/Los_Angeles all pass 344/344, and prettier and eslint are clean on the file.

isValidDate compared the parsed date against getUTC* values, but only the
ISO YYYY-MM-DD branch was actually parsed as UTC. The MM/DD/YYYY slash form
is parsed in the runtime's local time zone, so in positive-offset zones the
parsed instant rolled back to the previous calendar day and valid dates were
rejected.

Build the date with Date.UTC from the captured components so validation no
longer depends on the runtime time zone. The impossible-date guard (e.g.
Feb 31) and the ISO branch are unaffected.

Signed-off-by: Arpit Jain <arpitjain099@gmail.com>
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.

1 participant