diff --git a/client/src/components/Planner/PlaceInspector.tsx b/client/src/components/Planner/PlaceInspector.tsx index 47bc5a3e4..3efa4eec7 100644 --- a/client/src/components/Planner/PlaceInspector.tsx +++ b/client/src/components/Planner/PlaceInspector.tsx @@ -39,7 +39,7 @@ import { navigationTargetLabel, getNavigationTargets, openNavigationTarget } fro import { TRANSPORT_TYPES, getPlaceBookings } from '../../utils/dayMerge' import { NavigationMenu } from '../shared/NavigationMenu' import { centredMapCardBox } from '../Map/centredMapCard' -import { resolveOpenNow, resolvePlaceTimeZone, placeWeekdayIndex, type OpeningPeriod } from './placeOpenState' +import { resolveOpenNow, resolvePlaceTimeZone, getWeekdayIndex, type OpeningPeriod } from './placeOpenState' import { convertHoursLine } from './placeHoursFormat' import type { EndDayControlProps } from '../Roadtrip/EndDayControl' import VisitControls, { type RoadtripStayControl } from '../Roadtrip/VisitControls' @@ -136,13 +136,6 @@ function usePlaceDetails(googlePlaceId, osmId, language) { return details } -function getWeekdayIndex(dateStr, timeZone) { - // weekdayDescriptions[0] = Monday … [6] = Sunday - if (!dateStr) return placeWeekdayIndex(new Date(), timeZone) - const jsDay = new Date(dateStr + 'T12:00:00').getDay() - return jsDay === 0 ? 6 : jsDay - 1 -} - function formatFileSize(bytes) { if (!bytes) return '' if (bytes < 1024) return `${bytes} B` diff --git a/client/src/components/Planner/placeOpenState.test.ts b/client/src/components/Planner/placeOpenState.test.ts index 6b20168d2..2f6662f5e 100644 --- a/client/src/components/Planner/placeOpenState.test.ts +++ b/client/src/components/Planner/placeOpenState.test.ts @@ -1,4 +1,4 @@ -import { resolveOpenNow, resolvePlaceTimeZone, placeWeekdayIndex } from './placeOpenState'; +import { getWeekdayIndex, resolveOpenNow, resolvePlaceTimeZone, placeWeekdayIndex } from './placeOpenState'; import type { OpeningPeriod } from './placeOpenState'; // Seoul is UTC+9, Paris UTC+2 in summer — far enough apart that a viewer's clock and @@ -56,6 +56,18 @@ describe('placeWeekdayIndex', () => { }); }); +describe('getWeekdayIndex', () => { + it('FE-PLANNER-OPENSTATE-023: reads a plan day by its own date, Monday first', () => { + expect(getWeekdayIndex('2026-07-27', 'Asia/Seoul')).toBe(0); + expect(getWeekdayIndex('2026-08-02', null)).toBe(6); + }); + + it('FE-PLANNER-OPENSTATE-024: a day without a date reads today in the place timezone', () => { + expect(getWeekdayIndex(null, 'UTC')).toBe(placeWeekdayIndex(new Date(), 'UTC')); + expect(getWeekdayIndex(undefined, null)).toBe(placeWeekdayIndex(new Date(), null)); + }); +}); + describe('resolveOpenNow', () => { it('FE-PLANNER-OPENSTATE-010: judges the periods in the place timezone, not the viewer one', () => { // Sunday 20:00 UTC → Monday 05:00 in Seoul. The server said closed. diff --git a/client/src/components/Planner/placeOpenState.ts b/client/src/components/Planner/placeOpenState.ts index 764f624de..495fea861 100644 --- a/client/src/components/Planner/placeOpenState.ts +++ b/client/src/components/Planner/placeOpenState.ts @@ -51,6 +51,14 @@ export function placeWeekdayIndex(now: Date, timeZone: string | null): number { return jsDay === 0 ? 6 : jsDay - 1 } +/** Monday-based weekday index of a plan day's date; for a day without one, today's in `timeZone`. */ +export function getWeekdayIndex(dateStr: string | null | undefined, timeZone: string | null): number { + // weekdayDescriptions[0] = Monday … [6] = Sunday + if (!dateStr) return placeWeekdayIndex(new Date(), timeZone) + const jsDay = new Date(dateStr + 'T12:00:00').getDay() + return jsDay === 0 ? 6 : jsDay - 1 +} + /** Weekday (Sunday = 0), minute of day and calendar date of `now` in `timeZone`. */ function localParts(now: Date, timeZone: string | null): { day: number; minutes: number; date: string } | null { if (!timeZone) return null