From 33062bf051ac5c8bec7d3d0f3be249df4c35c2c6 Mon Sep 17 00:00:00 2001 From: Gil Glick Date: Mon, 8 Jun 2026 17:45:26 -0400 Subject: [PATCH 1/4] Times should be updated with local times --- .../CalendarPage/AvailabilityScheduleView.tsx | 10 +- .../pages/CalendarPage/CalendarWeekView.tsx | 3 +- .../Components/EventAvailabilityPage.tsx | 4 +- .../Components/ScheduleEventModal.tsx | 4 +- .../pages/CalendarPage/EventClickPopup.tsx | 4 +- .../CalendarPage/EventPartialInfoView.tsx | 4 +- .../CalendarPage/UpcomingMeetingsCard.tsx | 3 +- .../Availability/EditAvailability.tsx | 11 +- .../Availability/SingleAvailabilityView.tsx | 11 +- src/frontend/src/utils/design-review.utils.ts | 103 ++++++++++++++++++ 10 files changed, 143 insertions(+), 14 deletions(-) diff --git a/src/frontend/src/pages/CalendarPage/AvailabilityScheduleView.tsx b/src/frontend/src/pages/CalendarPage/AvailabilityScheduleView.tsx index 39d95d251f..b1a96a1363 100644 --- a/src/frontend/src/pages/CalendarPage/AvailabilityScheduleView.tsx +++ b/src/frontend/src/pages/CalendarPage/AvailabilityScheduleView.tsx @@ -1,7 +1,13 @@ import { Table, TableBody, TableCell, TableContainer, TableHead, TableRow, Typography } from '@mui/material'; import { Availability, Event, EventWithMembers, getDayOfWeek, getNextSevenDays, User } from 'shared'; import React, { useState } from 'react'; -import { enumToArray, getBackgroundColor, NUMBER_OF_TIME_SLOTS, REVIEW_TIMES } from '../../utils/design-review.utils'; +import { + enumToArray, + getBackgroundColor, + NUMBER_OF_TIME_SLOTS, + REVIEW_TIMES, + reviewTimesInCurrentTimeZone +} from '../../utils/design-review.utils'; import { datePipe } from '../../utils/pipes'; import EventTimeSlot from './Components/EventTimeSlot'; @@ -166,7 +172,7 @@ const AvailabilityScheduleView: React.FC = ({ - {time} + {reviewTimesInCurrentTimeZone(time)} {potentialDays.map((day, dayIndex) => { diff --git a/src/frontend/src/pages/CalendarPage/CalendarWeekView.tsx b/src/frontend/src/pages/CalendarPage/CalendarWeekView.tsx index 0ac4628318..9c9abc59b6 100644 --- a/src/frontend/src/pages/CalendarPage/CalendarWeekView.tsx +++ b/src/frontend/src/pages/CalendarPage/CalendarWeekView.tsx @@ -18,6 +18,7 @@ import { useCurrentUser } from '../../hooks/users.hooks'; import { getMutedColor } from '../../utils/calendar.utils'; import { getTeamTypeIcon } from './CalendarDayCard'; import { TaskClickContent } from './TaskClickPopup'; +import { formatHourInCurrentTimeZone } from '../../utils/design-review.utils'; // ─── Layout constants ──────────────────────────────────────────────────────── @@ -870,7 +871,7 @@ const CalendarWeekView: React.FC = ({ > {hour > 0 && ( - {formatHour(hour)} + {formatHourInCurrentTimeZone(formatHour(hour))} )} diff --git a/src/frontend/src/pages/CalendarPage/Components/EventAvailabilityPage.tsx b/src/frontend/src/pages/CalendarPage/Components/EventAvailabilityPage.tsx index ec0af70668..c36479e24f 100644 --- a/src/frontend/src/pages/CalendarPage/Components/EventAvailabilityPage.tsx +++ b/src/frontend/src/pages/CalendarPage/Components/EventAvailabilityPage.tsx @@ -32,6 +32,7 @@ import SingleAvailabilityModal from '../../SettingsPage/UserScheduleSettings/Ava import AvailabilityEditModal from '../../SettingsPage/UserScheduleSettings/Availability/AvailabilityEditModal'; import AvailabilityScheduleView from '../AvailabilityScheduleView'; import ScheduleEventModal from './ScheduleEventModal'; +import { formatHourInCurrentTimeZone } from '../../../utils/design-review.utils'; const isUserOnEvent = (user: User, event: EventWithMembers): boolean => { const isDirectMember = @@ -309,7 +310,8 @@ export const EventAvailabilityPage: React.FC = () => { {displaySlot.day.toLocaleDateString('en-US', { weekday: 'long', month: 'short', day: 'numeric' })} - {formatHour(displaySlot.startHour)} - {formatHour(displaySlot.endHour)} + {formatHourInCurrentTimeZone(formatHour(displaySlot.startHour))} -{' '} + {formatHourInCurrentTimeZone(formatHour(displaySlot.endHour))} {currentAvailableUsers.length}/{relevantUsers.length} available diff --git a/src/frontend/src/pages/CalendarPage/Components/ScheduleEventModal.tsx b/src/frontend/src/pages/CalendarPage/Components/ScheduleEventModal.tsx index a9a311b85d..07731b81c9 100644 --- a/src/frontend/src/pages/CalendarPage/Components/ScheduleEventModal.tsx +++ b/src/frontend/src/pages/CalendarPage/Components/ScheduleEventModal.tsx @@ -13,6 +13,7 @@ import { useToast } from '../../../hooks/toasts.hooks'; import { formatEventTime } from 'shared'; import { datePipe } from '../../../utils/pipes'; import { routes } from '../../../utils/routes'; +import { formatHourInCurrentTimeZone } from '../../../utils/design-review.utils'; interface ScheduleEventModalProps { open: boolean; @@ -72,7 +73,8 @@ const ScheduleEventModal: React.FC = ({ > {datePipe(selectedDay)} - {formatEventTime(startTime)} - {formatEventTime(endTime)} + {formatHourInCurrentTimeZone(formatEventTime(startTime))} - + {formatHourInCurrentTimeZone(formatEventTime(endTime))} diff --git a/src/frontend/src/pages/CalendarPage/EventClickPopup.tsx b/src/frontend/src/pages/CalendarPage/EventClickPopup.tsx index 2fba854432..cd0393e8b1 100644 --- a/src/frontend/src/pages/CalendarPage/EventClickPopup.tsx +++ b/src/frontend/src/pages/CalendarPage/EventClickPopup.tsx @@ -49,6 +49,7 @@ import { useToast } from '../../hooks/toasts.hooks'; import NERDeleteModal from '../../components/NERDeleteModal'; import NotificationsIcon from '@mui/icons-material/Notifications'; import { getPendingReason } from '../../utils/calendar.utils'; +import { formatHourInCurrentTimeZone } from '../../utils/design-review.utils'; export const getStatusIcon = (status: string, isLarge?: boolean) => { const statusIcons: Map = new Map([ @@ -278,7 +279,8 @@ export const EventClickContent: React.FC = ({ )} {dayOfWeek && !event.allDay && ( - {formatEventTime(event.startTime)} – {formatEventTime(event.endTime)} + {formatHourInCurrentTimeZone(formatEventTime(event.startTime))} –{' '} + {formatHourInCurrentTimeZone(formatEventTime(event.endTime))} )} {dayOfWeek && event.allDay && All day} diff --git a/src/frontend/src/pages/CalendarPage/EventPartialInfoView.tsx b/src/frontend/src/pages/CalendarPage/EventPartialInfoView.tsx index ae3ca1f2c8..1d1fd93539 100644 --- a/src/frontend/src/pages/CalendarPage/EventPartialInfoView.tsx +++ b/src/frontend/src/pages/CalendarPage/EventPartialInfoView.tsx @@ -7,6 +7,7 @@ import { getTeamTypeIcon } from './CalendarDayCard'; import { Typography } from '@mui/material'; import { getMutedColor } from '../../utils/calendar.utils'; +import { formatHourInCurrentTimeZone } from '../../utils/design-review.utils'; interface EventInfoProps { event: EventInstance; @@ -77,7 +78,8 @@ const EventPartialInfoView: React.FC = ({ event, eventTypes, cal ) : ( - {formatEventTime(event.startTime)} - {formatEventTime(event.endTime)} + {formatHourInCurrentTimeZone(formatEventTime(event.startTime))} -{' '} + {formatHourInCurrentTimeZone(formatEventTime(event.endTime))} )} diff --git a/src/frontend/src/pages/CalendarPage/UpcomingMeetingsCard.tsx b/src/frontend/src/pages/CalendarPage/UpcomingMeetingsCard.tsx index 2f7dc2e1a3..c10d721845 100644 --- a/src/frontend/src/pages/CalendarPage/UpcomingMeetingsCard.tsx +++ b/src/frontend/src/pages/CalendarPage/UpcomingMeetingsCard.tsx @@ -7,6 +7,7 @@ import { Calendar, EventInstance, EventType, formatEventTime } from 'shared'; import { datePipe } from '../../utils/pipes'; import { getMutedColor, getPendingReason } from '../../utils/calendar.utils'; +import { formatHourInCurrentTimeZone } from '../../utils/design-review.utils'; interface UpcomingMeetingProp { calendars: Calendar[]; @@ -72,7 +73,7 @@ const UpcomingMeetingsCard: React.FC = ({ event, calendars {/* Event Time */} - {formatEventTime(new Date(event.startTime))} + {formatHourInCurrentTimeZone(formatEventTime(new Date(event.startTime)))} diff --git a/src/frontend/src/pages/SettingsPage/UserScheduleSettings/Availability/EditAvailability.tsx b/src/frontend/src/pages/SettingsPage/UserScheduleSettings/Availability/EditAvailability.tsx index 10eed2bed8..f8e604a8da 100644 --- a/src/frontend/src/pages/SettingsPage/UserScheduleSettings/Availability/EditAvailability.tsx +++ b/src/frontend/src/pages/SettingsPage/UserScheduleSettings/Availability/EditAvailability.tsx @@ -10,7 +10,12 @@ import { useMediaQuery } from '@mui/material'; import { useEffect, useState } from 'react'; -import { HeatmapColors, enumToArray, REVIEW_TIMES } from '../../../../utils/design-review.utils'; +import { + HeatmapColors, + enumToArray, + REVIEW_TIMES, + reviewTimesInCurrentTimeZone +} from '../../../../utils/design-review.utils'; import { addDaysToDate, Availability, getDayOfWeek, getMostRecentAvailabilities } from 'shared'; import { datePipe } from '../../../../utils/pipes'; import NERArrows from '../../../../components/NERArrows'; @@ -191,10 +196,10 @@ const EditAvailability: React.FC = ({ {enumToArray(REVIEW_TIMES).map((time, timeIndex) => ( - + - {time} + {reviewTimesInCurrentTimeZone(time)} {currentlyDisplayedAvailabilities.map((availability, dayIndex) => { diff --git a/src/frontend/src/pages/SettingsPage/UserScheduleSettings/Availability/SingleAvailabilityView.tsx b/src/frontend/src/pages/SettingsPage/UserScheduleSettings/Availability/SingleAvailabilityView.tsx index 12c9dc7b3c..eb0bb38d3d 100644 --- a/src/frontend/src/pages/SettingsPage/UserScheduleSettings/Availability/SingleAvailabilityView.tsx +++ b/src/frontend/src/pages/SettingsPage/UserScheduleSettings/Availability/SingleAvailabilityView.tsx @@ -3,7 +3,12 @@ import { Availability, getDayOfWeek, getMostRecentAvailabilities } from 'shared' import { datePipe } from '../../../../utils/pipes'; import { useState, useEffect } from 'react'; import NERArrows from '../../../../components/NERArrows'; -import { enumToArray, REVIEW_TIMES, getBackgroundColor } from '../../../../utils/design-review.utils'; +import { + enumToArray, + REVIEW_TIMES, + getBackgroundColor, + reviewTimesInCurrentTimeZone +} from '../../../../utils/design-review.utils'; import EventTimeSlot from '../../../CalendarPage/Components/EventTimeSlot'; interface SingleAvailabilityViewProps { @@ -89,10 +94,10 @@ const SingleAvailabilityView: React.FC = ({ totalAv {enumToArray(REVIEW_TIMES).map((time, timeIndex) => ( - + - {time} + {reviewTimesInCurrentTimeZone(time)} {selectedTimes.map((availability, dayIndex) => { diff --git a/src/frontend/src/utils/design-review.utils.ts b/src/frontend/src/utils/design-review.utils.ts index 189bc0a42a..ca2a7fcc00 100644 --- a/src/frontend/src/utils/design-review.utils.ts +++ b/src/frontend/src/utils/design-review.utils.ts @@ -54,6 +54,109 @@ export const HeatmapColors = ['#D9D9D9', '#C1E0C1', '#9BE89B', '#7AE47A', '#45EF export const NUMBER_OF_TIME_SLOTS = enumToArray(REVIEW_TIMES).length * enumToArray(DAY_NAMES).length; +const monthToIndex = (month: MONTH_NAMES) => { + const monthToIndexMap: Map = new Map([ + [MONTH_NAMES.January, 0], + [MONTH_NAMES.February, 1], + [MONTH_NAMES.March, 2], + [MONTH_NAMES.April, 3], + [MONTH_NAMES.May, 4], + [MONTH_NAMES.June, 5], + [MONTH_NAMES.July, 6], + [MONTH_NAMES.August, 7], + [MONTH_NAMES.September, 8], + [MONTH_NAMES.October, 9], + [MONTH_NAMES.November, 10], + [MONTH_NAMES.December, 11] + ]); + return monthToIndexMap.get(month); +}; + +const dayToIndex = (weekday: DAY_NAMES) => { + const dayToIndexMap: Map = new Map([ + [DAY_NAMES.Sunday, 0], + [DAY_NAMES.Sunday, 1], + [DAY_NAMES.Sunday, 2], + [DAY_NAMES.Sunday, 3], + [DAY_NAMES.Sunday, 4], + [DAY_NAMES.Sunday, 5], + [DAY_NAMES.Sunday, 6] + ]); + return dayToIndexMap.get(weekday); +}; + +const nthWeekday = (year: number, month: MONTH_NAMES, weekday: DAY_NAMES, nth: number) => { + const monthIndex = monthToIndex(month); + const dayIndex = dayToIndex(weekday); + + const day = new Date(year, monthIndex!, 1); + const dayDiff = (dayIndex! - day.getDay() + 7) % 7; + + return new Date(year, month, 1 + dayDiff + (nth - 1) * 7); +}; + +const daylightSavings = () => { + const currDate = new Date(); + + const start = nthWeekday(currDate.getFullYear(), MONTH_NAMES.March, DAY_NAMES.Sunday, 2); + const end = nthWeekday(currDate.getFullYear(), MONTH_NAMES.November, DAY_NAMES.Sunday, 1); + return currDate >= start && currDate < end; +}; + +// converts a REVIEW_TIME (That is in string form) to the user's current time +export const reviewTimesInCurrentTimeZone = (time: string) => { + const UTCOffset = -new Date().getTimezoneOffset() / 60; + const EST = daylightSavings() ? -4 : -5; + const userOffset = UTCOffset - EST; + return offsetReviewTime(time, userOffset); +}; + +const isTwoDigitHour = (time: string) => !isNaN(Number(time.charAt(1))); + +const offsetReviewTime = (time: string, offset: number) => { + // get initialTime + const startTime = Number(time.charAt(0) + (isTwoDigitHour(time) ? time.charAt(1) : '')); + + let newStartTime = (startTime + offset + 12) % 12; + if (newStartTime === 0) newStartTime = 12; + let newEndTime = (startTime + 1 + offset + 12) % 12; + if (newEndTime === 0) newEndTime = 12; + + return newStartTime + '-' + newEndTime + ' ' + AMorPM(time, offset); +}; + +const AMorPM = (time: string, offset: number) => { + const startTime = Number(time.charAt(0) + (isTwoDigitHour(time) ? time.charAt(1) : '')); + let AMorPM = time.charAt(time.length - 2) + time.charAt(time.length - 1); + + const flipAMPM = () => { + if (AMorPM === 'AM') return 'PM'; + return 'AM'; + }; + + // if odd, the AM / PM should be flipped + const doFlip = Math.floor((startTime + offset - 1) / 12) % 2 === 1; + if (doFlip) AMorPM = flipAMPM(); + + return AMorPM; +}; + +export const formatHourInCurrentTimeZone = (time: string) => { + const UTCOffset = -new Date().getTimezoneOffset() / 60; + const EST = daylightSavings() ? -4 : -5; + const userOffset = UTCOffset - EST; + return offsetFormatHour(time, userOffset); +}; + +const offsetFormatHour = (time: string, offset: number) => { + const hourTime = Number(time.charAt(0) + (isTwoDigitHour(time) ? time.charAt(1) : '')); + + let newHourTime = (((hourTime + offset) % 12) + 12) % 12; + if (newHourTime === 0) newHourTime = 12; + + return newHourTime + ':00 ' + AMorPM(time, offset); +}; + export const getBackgroundColor = (frequency: number = 0, totalUsers: number): string => { if (frequency === 0) return HeatmapColors[0]; if (frequency >= totalUsers) return HeatmapColors[5]; From f9877c0530d57452d29f0341cb4e0641d72beb5c Mon Sep 17 00:00:00 2001 From: Gil Glick Date: Mon, 8 Jun 2026 19:05:03 -0400 Subject: [PATCH 2/4] days update accordingly too --- .../Components/EventAvailabilityPage.tsx | 13 ++++++++++-- .../Components/ScheduleEventModal.tsx | 14 +++++++++---- src/frontend/src/utils/design-review.utils.ts | 20 ++++++++++++------- 3 files changed, 34 insertions(+), 13 deletions(-) diff --git a/src/frontend/src/pages/CalendarPage/Components/EventAvailabilityPage.tsx b/src/frontend/src/pages/CalendarPage/Components/EventAvailabilityPage.tsx index c36479e24f..a61a8b61ab 100644 --- a/src/frontend/src/pages/CalendarPage/Components/EventAvailabilityPage.tsx +++ b/src/frontend/src/pages/CalendarPage/Components/EventAvailabilityPage.tsx @@ -32,7 +32,7 @@ import SingleAvailabilityModal from '../../SettingsPage/UserScheduleSettings/Ava import AvailabilityEditModal from '../../SettingsPage/UserScheduleSettings/Availability/AvailabilityEditModal'; import AvailabilityScheduleView from '../AvailabilityScheduleView'; import ScheduleEventModal from './ScheduleEventModal'; -import { formatHourInCurrentTimeZone } from '../../../utils/design-review.utils'; +import { formatHourInCurrentTimeZone, offsetDate } from '../../../utils/design-review.utils'; const isUserOnEvent = (user: User, event: EventWithMembers): boolean => { const isDirectMember = @@ -303,11 +303,20 @@ export const EventAvailabilityPage: React.FC = () => { {/* Date/Time display */} {(() => { const displaySlot = currentHoveredSlot || selectedSlot; + const specificTime = () => { + const specificDate = new Date(displaySlot!.day); + specificDate.setHours(displaySlot!.startHour); + return specificDate; + }; if (displaySlot) { return ( - {displaySlot.day.toLocaleDateString('en-US', { weekday: 'long', month: 'short', day: 'numeric' })} + {offsetDate(specificTime()).toLocaleDateString('en-US', { + weekday: 'long', + month: 'short', + day: 'numeric' + })} {formatHourInCurrentTimeZone(formatHour(displaySlot.startHour))} -{' '} diff --git a/src/frontend/src/pages/CalendarPage/Components/ScheduleEventModal.tsx b/src/frontend/src/pages/CalendarPage/Components/ScheduleEventModal.tsx index 07731b81c9..e93681ad95 100644 --- a/src/frontend/src/pages/CalendarPage/Components/ScheduleEventModal.tsx +++ b/src/frontend/src/pages/CalendarPage/Components/ScheduleEventModal.tsx @@ -13,7 +13,7 @@ import { useToast } from '../../../hooks/toasts.hooks'; import { formatEventTime } from 'shared'; import { datePipe } from '../../../utils/pipes'; import { routes } from '../../../utils/routes'; -import { formatHourInCurrentTimeZone } from '../../../utils/design-review.utils'; +import { formatHourInCurrentTimeZone, offsetDate } from '../../../utils/design-review.utils'; interface ScheduleEventModalProps { open: boolean; @@ -41,10 +41,10 @@ const ScheduleEventModal: React.FC = ({ const { mutateAsync: scheduleEvent, isLoading } = useScheduleEvent(eventId); // Compute the full start and end times - const startTime = new Date(selectedDay); + const startTime = new Date(offsetDate(selectedDay)); startTime.setHours(startHour, 0, 0, 0); - const endTime = new Date(selectedDay); + const endTime = new Date(offsetDate(selectedDay)); endTime.setHours(endHour, 0, 0, 0); const handleConfirm = async () => { @@ -60,6 +60,12 @@ const ScheduleEventModal: React.FC = ({ } }; + const dateToShow = () => { + const newDate = new Date(selectedDay); + newDate.setHours(startHour); + return newDate; + }; + return ( @@ -71,7 +77,7 @@ const ScheduleEventModal: React.FC = ({ - {datePipe(selectedDay)} + {datePipe(offsetDate(dateToShow()))} {formatHourInCurrentTimeZone(formatEventTime(startTime))} - {formatHourInCurrentTimeZone(formatEventTime(endTime))} diff --git a/src/frontend/src/utils/design-review.utils.ts b/src/frontend/src/utils/design-review.utils.ts index ca2a7fcc00..63530909b0 100644 --- a/src/frontend/src/utils/design-review.utils.ts +++ b/src/frontend/src/utils/design-review.utils.ts @@ -103,12 +103,21 @@ const daylightSavings = () => { return currDate >= start && currDate < end; }; -// converts a REVIEW_TIME (That is in string form) to the user's current time -export const reviewTimesInCurrentTimeZone = (time: string) => { +export const userOffsetTime = () => { const UTCOffset = -new Date().getTimezoneOffset() / 60; const EST = daylightSavings() ? -4 : -5; const userOffset = UTCOffset - EST; - return offsetReviewTime(time, userOffset); + return userOffset; +}; + +export const offsetDate = (date: Date) => { + const hoursOffset = userOffsetTime(); + return new Date(date.getFullYear(), date.getMonth(), date.getDate(), date.getHours() + hoursOffset); +}; + +// converts a REVIEW_TIME (That is in string form) to the user's current time +export const reviewTimesInCurrentTimeZone = (time: string) => { + return offsetReviewTime(time, userOffsetTime()); }; const isTwoDigitHour = (time: string) => !isNaN(Number(time.charAt(1))); @@ -142,10 +151,7 @@ const AMorPM = (time: string, offset: number) => { }; export const formatHourInCurrentTimeZone = (time: string) => { - const UTCOffset = -new Date().getTimezoneOffset() / 60; - const EST = daylightSavings() ? -4 : -5; - const userOffset = UTCOffset - EST; - return offsetFormatHour(time, userOffset); + return offsetFormatHour(time, userOffsetTime()); }; const offsetFormatHour = (time: string, offset: number) => { From b870ded2f50b1196ed2007e8eaa7a7b01b167d4e Mon Sep 17 00:00:00 2001 From: Gil Glick Date: Tue, 9 Jun 2026 09:19:45 -0400 Subject: [PATCH 3/4] fixed am/pm logic --- src/frontend/src/utils/design-review.utils.ts | 53 +++---------------- 1 file changed, 7 insertions(+), 46 deletions(-) diff --git a/src/frontend/src/utils/design-review.utils.ts b/src/frontend/src/utils/design-review.utils.ts index 63530909b0..103b1b2510 100644 --- a/src/frontend/src/utils/design-review.utils.ts +++ b/src/frontend/src/utils/design-review.utils.ts @@ -54,43 +54,9 @@ export const HeatmapColors = ['#D9D9D9', '#C1E0C1', '#9BE89B', '#7AE47A', '#45EF export const NUMBER_OF_TIME_SLOTS = enumToArray(REVIEW_TIMES).length * enumToArray(DAY_NAMES).length; -const monthToIndex = (month: MONTH_NAMES) => { - const monthToIndexMap: Map = new Map([ - [MONTH_NAMES.January, 0], - [MONTH_NAMES.February, 1], - [MONTH_NAMES.March, 2], - [MONTH_NAMES.April, 3], - [MONTH_NAMES.May, 4], - [MONTH_NAMES.June, 5], - [MONTH_NAMES.July, 6], - [MONTH_NAMES.August, 7], - [MONTH_NAMES.September, 8], - [MONTH_NAMES.October, 9], - [MONTH_NAMES.November, 10], - [MONTH_NAMES.December, 11] - ]); - return monthToIndexMap.get(month); -}; - -const dayToIndex = (weekday: DAY_NAMES) => { - const dayToIndexMap: Map = new Map([ - [DAY_NAMES.Sunday, 0], - [DAY_NAMES.Sunday, 1], - [DAY_NAMES.Sunday, 2], - [DAY_NAMES.Sunday, 3], - [DAY_NAMES.Sunday, 4], - [DAY_NAMES.Sunday, 5], - [DAY_NAMES.Sunday, 6] - ]); - return dayToIndexMap.get(weekday); -}; - const nthWeekday = (year: number, month: MONTH_NAMES, weekday: DAY_NAMES, nth: number) => { - const monthIndex = monthToIndex(month); - const dayIndex = dayToIndex(weekday); - - const day = new Date(year, monthIndex!, 1); - const dayDiff = (dayIndex! - day.getDay() + 7) % 7; + const day = new Date(year, month!, 1); + const dayDiff = (weekday! - day.getDay() + 7) % 7; return new Date(year, month, 1 + dayDiff + (nth - 1) * 7); }; @@ -136,18 +102,13 @@ const offsetReviewTime = (time: string, offset: number) => { const AMorPM = (time: string, offset: number) => { const startTime = Number(time.charAt(0) + (isTwoDigitHour(time) ? time.charAt(1) : '')); - let AMorPM = time.charAt(time.length - 2) + time.charAt(time.length - 1); - - const flipAMPM = () => { - if (AMorPM === 'AM') return 'PM'; - return 'AM'; - }; + const AMorPM = time.charAt(time.length - 2) + time.charAt(time.length - 1); + const startTime24Hour = startTime + (AMorPM === 'PM' && startTime !== 12 ? 12 : 0); - // if odd, the AM / PM should be flipped - const doFlip = Math.floor((startTime + offset - 1) / 12) % 2 === 1; - if (doFlip) AMorPM = flipAMPM(); + const newTime = startTime24Hour + offset; + const newHour = (newTime + 24) % 24; - return AMorPM; + return newHour >= 12 ? 'PM' : 'AM'; }; export const formatHourInCurrentTimeZone = (time: string) => { From 5c370010995eb4e5e6f03a553356dd5a5410a80e Mon Sep 17 00:00:00 2001 From: Gil Glick Date: Thu, 11 Jun 2026 12:26:04 -0400 Subject: [PATCH 4/4] used Intl for daylight savings --- .../Components/ScheduleEventModal.tsx | 5 ++--- .../CalendarPage/EventPartialInfoView.tsx | 4 +--- src/frontend/src/utils/design-review.utils.ts | 21 ++++++++----------- 3 files changed, 12 insertions(+), 18 deletions(-) diff --git a/src/frontend/src/pages/CalendarPage/Components/ScheduleEventModal.tsx b/src/frontend/src/pages/CalendarPage/Components/ScheduleEventModal.tsx index e93681ad95..d40863040f 100644 --- a/src/frontend/src/pages/CalendarPage/Components/ScheduleEventModal.tsx +++ b/src/frontend/src/pages/CalendarPage/Components/ScheduleEventModal.tsx @@ -13,7 +13,7 @@ import { useToast } from '../../../hooks/toasts.hooks'; import { formatEventTime } from 'shared'; import { datePipe } from '../../../utils/pipes'; import { routes } from '../../../utils/routes'; -import { formatHourInCurrentTimeZone, offsetDate } from '../../../utils/design-review.utils'; +import { offsetDate } from '../../../utils/design-review.utils'; interface ScheduleEventModalProps { open: boolean; @@ -79,8 +79,7 @@ const ScheduleEventModal: React.FC = ({ > {datePipe(offsetDate(dateToShow()))} - {formatHourInCurrentTimeZone(formatEventTime(startTime))} - - {formatHourInCurrentTimeZone(formatEventTime(endTime))} + {formatEventTime(startTime)} -{formatEventTime(endTime)} diff --git a/src/frontend/src/pages/CalendarPage/EventPartialInfoView.tsx b/src/frontend/src/pages/CalendarPage/EventPartialInfoView.tsx index 1d1fd93539..ae3ca1f2c8 100644 --- a/src/frontend/src/pages/CalendarPage/EventPartialInfoView.tsx +++ b/src/frontend/src/pages/CalendarPage/EventPartialInfoView.tsx @@ -7,7 +7,6 @@ import { getTeamTypeIcon } from './CalendarDayCard'; import { Typography } from '@mui/material'; import { getMutedColor } from '../../utils/calendar.utils'; -import { formatHourInCurrentTimeZone } from '../../utils/design-review.utils'; interface EventInfoProps { event: EventInstance; @@ -78,8 +77,7 @@ const EventPartialInfoView: React.FC = ({ event, eventTypes, cal ) : ( - {formatHourInCurrentTimeZone(formatEventTime(event.startTime))} -{' '} - {formatHourInCurrentTimeZone(formatEventTime(event.endTime))} + {formatEventTime(event.startTime)} - {formatEventTime(event.endTime)} )} diff --git a/src/frontend/src/utils/design-review.utils.ts b/src/frontend/src/utils/design-review.utils.ts index 103b1b2510..1b0e32490a 100644 --- a/src/frontend/src/utils/design-review.utils.ts +++ b/src/frontend/src/utils/design-review.utils.ts @@ -54,24 +54,21 @@ export const HeatmapColors = ['#D9D9D9', '#C1E0C1', '#9BE89B', '#7AE47A', '#45EF export const NUMBER_OF_TIME_SLOTS = enumToArray(REVIEW_TIMES).length * enumToArray(DAY_NAMES).length; -const nthWeekday = (year: number, month: MONTH_NAMES, weekday: DAY_NAMES, nth: number) => { - const day = new Date(year, month!, 1); - const dayDiff = (weekday! - day.getDay() + 7) % 7; +const ESTOffset = () => { + const parts = new Intl.DateTimeFormat('en', { + timeZone: 'America/New_York', + timeZoneName: 'shortOffset' + }).formatToParts(new Date()); - return new Date(year, month, 1 + dayDiff + (nth - 1) * 7); -}; - -const daylightSavings = () => { - const currDate = new Date(); + const GMTTime = parts.find((t) => t.type === 'timeZoneName'); + const offsetEST = Number(GMTTime!.value.replace('GMT', '')); - const start = nthWeekday(currDate.getFullYear(), MONTH_NAMES.March, DAY_NAMES.Sunday, 2); - const end = nthWeekday(currDate.getFullYear(), MONTH_NAMES.November, DAY_NAMES.Sunday, 1); - return currDate >= start && currDate < end; + return offsetEST; }; export const userOffsetTime = () => { const UTCOffset = -new Date().getTimezoneOffset() / 60; - const EST = daylightSavings() ? -4 : -5; + const EST = ESTOffset(); const userOffset = UTCOffset - EST; return userOffset; };