Skip to content

docs(SDK-810): date normalization audit and solution spike#1764

Draft
mariechatfield wants to merge 3 commits into
mainfrom
marie/SDK-810-spike-on-dates
Draft

docs(SDK-810): date normalization audit and solution spike#1764
mariechatfield wants to merge 3 commits into
mainfrom
marie/SDK-810-spike-on-dates

Conversation

@mariechatfield
Copy link
Copy Markdown
Contributor

@mariechatfield mariechatfield commented May 11, 2026

Summary

Audit the existing usage of dates, date normalization functions, and and logic across the app and identify an approach to make date handling more consistent and avoid timezone errors when going between formats.

Related

Consolidates the timezone bug audit and two-phase solution proposal
into rfc/2026-05-11--date-normalization.md. Covers known unsafe
callsites, the Phase 1 convention-hardening plan, and the Phase 2
CalendarDate migration approach.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Comment thread rfc/2026-05-11--date-normalization.md Outdated
| `Payroll/helpers.ts` (compensation effective dates) | `new Date(effectiveDate)` on string → UTC midnight |
| `useEmployeeStateTaxesForm` deserialization | `new Date(trimmed)` on string → UTC midnight |
| `CalendarPreview.tsx:32` | `new Date(dateValue.toString())` — behavior varies |
| `useEmployeeDetailsForm`, `useHomeAddressForm`, `useWorkAddressForm` | `new RFCDate(new Date(string))` — UTC roundtrip (**live data-integrity bug** for UTC+ users: wrong birth date / effective date sent to API) |
Copy link
Copy Markdown
Contributor Author

@mariechatfield mariechatfield May 11, 2026

Choose a reason for hiding this comment

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

Found an active bug for these hooks by auditing dates. I'll create a new bug ticket (https://gustohq.atlassian.net/browse/SDK-828) and work on those / add tests so we can address it independently of the rest of the date normalization or migration.

- Correct the RFCDate section: new RFCDate(new Date(string)) is a
  harmless UTC roundtrip when the input is a YYYY-MM-DD string, not a
  live bug. Fields in the three affected hooks are typed as z.iso.date()
  so the UTC→UTC cancellation holds.
- Demote the three-hook cleanup from highest-priority bug fix to optional
  cleanup in the Phase 1 implementation order.
- Note that SDK-828 (PR #1767) fixes formatDateToStringDate (Phase 1
  step 2 partial) but leaves callsite migration and deserialization fixes
  (steps 3–4) as the remaining live bugs.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Copy link
Copy Markdown
Member

@serikjensen serikjensen left a comment

Choose a reason for hiding this comment

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

Excellent findings! sounds like there's an opportunity to just standardize on CalendarDate for our internal operations.

One thing i didn't see included was defaultValues. Some of the date pickers that live in forms allow for defaultValues setting the date via props. This caused a BQE bug recently. So if we did make the move to standardize on a specific format, i might recommend iso string sans time as the universal value for all the things partner facing for clarity. That would include adapters with date values as outlined below as well as default values for forms.

My other question is if we migrate to CalendarDate for all internal operations, does this play well with existing endpoints? I know we have RFCDate that is used as a step in api requests so wondering if that could be updated to just get a date string from CalendarDate, or if there's a reason we can't just standardize on the dates that the API is using

Thanks for putting this together!

Comment thread rfc/2026-05-11--date-normalization.md Outdated
Comment on lines +167 to +175
### Adapter Boundary Is Fixed

`DatePickerProps` and `DateRangePickerProps` are the partner-facing API and must keep `Date` at the boundary. Everything above this boundary is internal and can change.

Conversions happen at two edges:

- **Inbound** (adapter → form): `value.getFullYear/getMonth/getDate` → `new CalendarDate(...)` — reads local, safe
- **Outbound** (form → adapter): `new Date(cd.year, cd.month - 1, cd.day)` — local midnight, safe
- **Display**: `cd.toDate(getLocalTimeZone()).toLocaleDateString(...)`
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

On this one, i'm wondering if we should just standardize on iso string without time components as the external partner contract. We could add some redundancy to where we parse the string .split('T')[0] to ensure we only get the date portion?

My thought here is that the time components could still introduce bugs depending on where the partner is creating the Date object? So wondering if we just need to get out of the Date game here altogether

It'd be a breaking change, but i think the partner migration would be pretty straightforward.

Thoughts?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

From our standup conversation:

  • Switching the API at the component adapter to a string with a specific format seems like a reasonable suggestion
  • I need to do some research on the internalized/date library to confirm we want to double down on it outside of react-aria
  • I will update this RFC and then open it for review, but we don't expect to actually make any of the recommended changes immediately (apart from the bugfix that's already been merged)

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