fix: convert appointment time from UTC to local time for display#407
fix: convert appointment time from UTC to local time for display#407Cy-fox wants to merge 2 commits intoneed4deed-org:developfrom
Conversation
Closes need4deed-org#393 Appointment times were stored as UTC strings by the API but displayed without timezone conversion, causing times to appear 2 hours early for users in CEST (UTC+2). Changes: - parseTime in AccompanyingDetails helpers now converts UTC HH:mm strings to local time using setUTCHours before extracting via toTimeString - formatAccompanyingDate in OpportunityCard.helpers now formats the date with date-fns and applies the same UTC-to-local conversion for the time
nadavosa
left a comment
There was a problem hiding this comment.
PR #407 Review: Convert appointment time from UTC to local time for display
The display-only paths (OpportunityCard card view, AccompanyingDetailsDisplay) are correct. However there is a critical bug in the edit flow:
Critical: Round-trip data corruption in the edit form
parseTime in AccompanyingDetails/helpers.ts is used by getInitialFormValues to populate the React Hook Form default values, including appointmentTime. The <TimeInput> is pre-filled with the already-converted local time. On submit, AccompanyingDetails.tsx sends values.appointmentTime directly back to the API unchanged. This means:
- API stores
08:30(UTC) parseTimeconverts it to10:30(CEST local)- User opens edit form — sees
10:30✓ - User saves without changing — API receives
10:30instead of08:30 - Next read:
parseTimeconverts10:30→12:30
Each save shifts the appointment time forward by the UTC offset. The fix should only convert for display, not for form state — either keep the raw UTC string in form state and convert only in display components, or convert back to UTC before submitting.
Medium: Duplicated conversion logic
The same 5-line UTC-to-local block appears in both OpportunityCard.helpers.tsx and AccompanyingDetails/helpers.ts. Should be extracted into a shared utility.
Low: toTimeString().slice(0, 5) relies on implementation-defined format
Works in all major browsers but String(d.getHours()).padStart(2, "0") + ":" + String(d.getMinutes()).padStart(2, "0") would be more explicit.
Info: DST awareness
When only HH:mm is stored without a date, DST transitions (summer vs winter) may produce a 1-hour discrepancy. Inherent limitation of the storage format.
…hared utility - Extract utcHhmmToLocal() into src/utils to avoid duplicated conversion logic - Revert parseTime to a passthrough so form state keeps the raw UTC string - Apply UTC-to-local conversion only in AccompanyingDetailsDisplay and OpportunityCard.helpers (display-only paths), preventing time drift on save - Use padStart formatting instead of toTimeString().slice(0, 5)
Closes #393
Appointment times were stored as UTC HH:mm strings by the API but rendered without any timezone conversion, causing them to appear 2 hours early for users in CEST (UTC+2). For example, an appointment at 10:30 CEST would show as 08:30.
Changes:
parseTimeinAccompanyingDetailshelpers now converts a UTC HH:mm string to local time by setting UTC hours on a Date object and extracting viatoTimeString, so the profile detail view shows the correct local timeformatAccompanyingDatein OpportunityCard.helpers now formats the rawappointmentDateISO string with date-fns and applies the same UTC-to-local conversion forappointmentTime, so the card view shows a human-readable date and the correct local time