Skip to content

fix: convert appointment time from UTC to local time for display#407

Closed
Cy-fox wants to merge 2 commits intoneed4deed-org:developfrom
Cy-fox:fix/appointment-time-utc-display-393
Closed

fix: convert appointment time from UTC to local time for display#407
Cy-fox wants to merge 2 commits intoneed4deed-org:developfrom
Cy-fox:fix/appointment-time-utc-display-393

Conversation

@Cy-fox
Copy link
Copy Markdown
Collaborator

@Cy-fox Cy-fox commented Apr 27, 2026

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:

  • parseTime in AccompanyingDetails helpers now converts a UTC HH:mm string to local time by setting UTC hours on a Date object and extracting via toTimeString, so the profile detail view shows the correct local time
  • formatAccompanyingDate in OpportunityCard.helpers now formats the raw appointmentDate ISO string with date-fns and applies the same UTC-to-local conversion for appointmentTime, so the card view shows a human-readable date and the correct local time

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
@Cy-fox Cy-fox requested a review from HansKre April 27, 2026 10:05
Copy link
Copy Markdown
Collaborator

@nadavosa nadavosa left a comment

Choose a reason for hiding this comment

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

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:

  1. API stores 08:30 (UTC)
  2. parseTime converts it to 10:30 (CEST local)
  3. User opens edit form — sees 10:30
  4. User saves without changing — API receives 10:30 instead of 08:30
  5. Next read: parseTime converts 10:3012: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)
@nadavosa nadavosa closed this Apr 30, 2026
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.

Fix: Accompanying opportunity appointment time displays 2 hours early

2 participants