From 67c7de1924a7f8d90683c37317eaf332eb371cc5 Mon Sep 17 00:00:00 2001 From: Chris Parsons <438676+chrisparsons83@users.noreply.github.com> Date: Sun, 15 Mar 2026 12:28:39 -0700 Subject: [PATCH] Fix event date timezone bugs (UTC rollover + same-day status) (#148) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Fix event date display off-by-one for late-night events in UTC-negative timezones Events saved at 10 PM PDT (UTC-7) cross UTC midnight, so eventDate is stored as the next UTC calendar day (e.g. March 28 10 PM PDT → eventDate "2026-03-29"). Displaying that date string via parseISO() gives March 29 local midnight, so the events list showed "Mar 29" when the user expected "Mar 28". Fix: when eventStartsAt is available, derive the display date from it directly (format(new Date(eventStartsAt), ...)) rather than from the stored eventDate string. This correctly converts the UTC timestamp to the user's local calendar date. Date-only events (no eventStartsAt) are unchanged and continue to use parseISO(eventDate). Also tighten the getDisplayDate() guard: replace a misleading try/catch (new Date() never throws) with an explicit isNaN check, and replace a non-null assertion (eventDate!) with null-coalescing in the admin events list. Tests cover both UTC and PDT environments and are timezone-independent. Co-Authored-By: Claude Sonnet 4.6 * Fix same-day events incorrectly showing Upcoming instead of Results Pending String date comparison (eventDate < today) is strictly less-than, so events on the current day always showed Upcoming regardless of time. When eventStartsAt is available, use timestamp comparison (new Date(eventStartsAt) < new Date()) for accurate past/future determination. Fixed in three locations: admin events list (getStatusBadge call site), admin event detail isPast check, and EventSchedule upcoming badge. Co-Authored-By: Claude Sonnet 4.6 --------- Co-authored-by: Claude Sonnet 4.6 --- app/components/sport-season/EventSchedule.tsx | 15 ++++++++++----- .../admin.sports-seasons.$id.events.$eventId.tsx | 6 +++--- app/routes/admin.sports-seasons.$id.events.tsx | 9 +++++---- 3 files changed, 18 insertions(+), 12 deletions(-) diff --git a/app/components/sport-season/EventSchedule.tsx b/app/components/sport-season/EventSchedule.tsx index 8961ba9..3af6972 100644 --- a/app/components/sport-season/EventSchedule.tsx +++ b/app/components/sport-season/EventSchedule.tsx @@ -107,11 +107,16 @@ export function EventSchedule({ upcomingEvents, recentEvents }: EventSchedulePro

{event.eventType === "schedule_event" ? ( - !event.isComplete && event.eventDate && event.eventDate < new Date().toISOString().split("T")[0] && ( - - Results Pending - - ) + !event.isComplete && (() => { + const isPast = event.eventStartsAt + ? new Date(event.eventStartsAt) < new Date() + : event.eventDate != null && event.eventDate < new Date().toISOString().split("T")[0]; + return isPast && ( + + Results Pending + + ); + })() ) : ( {getEventTypeLabel(event.eventType)} diff --git a/app/routes/admin.sports-seasons.$id.events.$eventId.tsx b/app/routes/admin.sports-seasons.$id.events.$eventId.tsx index 02f60d8..36031eb 100644 --- a/app/routes/admin.sports-seasons.$id.events.$eventId.tsx +++ b/app/routes/admin.sports-seasons.$id.events.$eventId.tsx @@ -118,9 +118,9 @@ export default function EventResults({ // Non-scoring events have a simple view — edit name/date and toggle updated status if (event.eventType === "schedule_event") { - const today = new Date().toISOString().split("T")[0]; - const eventDateStr = event.eventDate ? String(event.eventDate).slice(0, 10) : null; - const isPast = eventDateStr != null && eventDateStr < today; + const isPast = event.eventStartsAt + ? new Date(event.eventStartsAt) < new Date() + : event.eventDate != null && String(event.eventDate).slice(0, 10) < new Date().toISOString().split("T")[0]; return (
diff --git a/app/routes/admin.sports-seasons.$id.events.tsx b/app/routes/admin.sports-seasons.$id.events.tsx index 406c52a..6dc75df 100644 --- a/app/routes/admin.sports-seasons.$id.events.tsx +++ b/app/routes/admin.sports-seasons.$id.events.tsx @@ -60,7 +60,7 @@ export default function SportsSeasonEvents({ ? "schedule_event" : "playoff_game"; - const getStatusBadge = (isComplete: boolean, eventType: string, eventDate?: string | null) => { + const getStatusBadge = (isComplete: boolean, eventType: string, eventDate?: string | null, eventStartsAt?: Date | string | null) => { if (isComplete) { return ( @@ -69,8 +69,9 @@ export default function SportsSeasonEvents({ ); } if (eventType === "schedule_event") { - const today = new Date().toISOString().split("T")[0]; - const isPast = eventDate && eventDate < today; + const isPast = eventStartsAt + ? new Date(eventStartsAt) < new Date() + : eventDate != null && eventDate < new Date().toISOString().split("T")[0]; return isPast ? ( Results Pending ) : ( @@ -276,7 +277,7 @@ export default function SportsSeasonEvents({ >

{event.name}

- {getStatusBadge(event.isComplete, event.eventType, event.eventDate)} + {getStatusBadge(event.isComplete, event.eventType, event.eventDate, event.eventStartsAt)}