diff --git a/client/src/components/Planner/DayPlanSidebar.test.tsx b/client/src/components/Planner/DayPlanSidebar.test.tsx
index 91e895ee2..c127d57dd 100644
--- a/client/src/components/Planner/DayPlanSidebar.test.tsx
+++ b/client/src/components/Planner/DayPlanSidebar.test.tsx
@@ -1040,6 +1040,28 @@ describe('DayPlanSidebar', () => {
// ── Multi-day transport span phases ────────────────────────────────────
+ it('FE-PLANNER-DAYPLAN-233: a leg names its time zone behind a globe, not as text beside the route', async () => {
+ const day1 = buildDay({ id: 10, date: '2025-06-01', title: 'Out' })
+ const day2 = buildDay({ id: 11, date: '2025-06-02', title: 'In' })
+ const flight = buildReservation({
+ id: 202, type: 'flight', title: 'Overnight',
+ reservation_time: '2025-06-01T22:00:00',
+ reservation_end_time: '2025-06-02T06:00:00',
+ day_id: 10,
+ end_day_id: 11,
+ metadata: JSON.stringify({ departure_timezone: 'Europe/Berlin', arrival_timezone: 'America/New_York' }),
+ } as any)
+ const { container } = render()
+
+ // Written out, the zone took the width the route needed.
+ expect(screen.queryByText(/Europe\/Berlin|America\/New_York/)).not.toBeInTheDocument()
+ const globes = container.querySelectorAll('[data-dp="transport-timezone"]')
+ expect([...globes].map(g => g.getAttribute('aria-label'))).toEqual(['Europe/Berlin', 'America/New_York'])
+
+ fireEvent.mouseEnter(globes[0])
+ expect(await screen.findByText('Europe/Berlin')).toBeInTheDocument()
+ })
+
it('FE-PLANNER-DAYPLAN-045: multi-day flight shows departure label on first day', () => {
const day1 = buildDay({ id: 10, date: '2025-06-01', title: 'Departure' })
const day2 = buildDay({ id: 11, date: '2025-06-02', title: 'Arrival' })
diff --git a/client/src/components/Planner/DayPlanSidebar.tsx b/client/src/components/Planner/DayPlanSidebar.tsx
index 7f359cf39..16ef1d8f7 100644
--- a/client/src/components/Planner/DayPlanSidebar.tsx
+++ b/client/src/components/Planner/DayPlanSidebar.tsx
@@ -7,7 +7,7 @@ declare global { interface Window { __dragData: DragDataPayload | null } }
import React, { useState, useEffect, useLayoutEffect, useRef, useMemo, useCallback } from 'react'
import { avatarSrc } from '../../utils/avatarSrc'
import { safeHttpUrl } from '../../utils/safeUrl'
-import { ChevronDown, ChevronRight, ChevronUp, Compass, Navigation, RotateCcw, ExternalLink, Pencil, GripVertical, Ticket, Plus, FileText, Trash2, Car, Lock, Hotel, Footprints, Route as RouteIcon, Bookmark, StickyNote, TramFront, Zap, MapPin, Wallet } from 'lucide-react'
+import { ChevronDown, ChevronRight, ChevronUp, Compass, Navigation, RotateCcw, ExternalLink, Pencil, GripVertical, Ticket, Plus, FileText, Trash2, Car, Lock, Hotel, Footprints, Route as RouteIcon, Bookmark, StickyNote, TramFront, Zap, MapPin, Wallet, Globe } from 'lucide-react'
import { type PickedPlace } from './TransitSearchPanel'
import { buildTransitLeg, buildTransitNameIndex } from './transitLeg'
import { assignmentsApi, reservationsApi, daysApi } from '../../api/client'
@@ -2559,15 +2559,24 @@ const DayPlanSidebar = React.memo(function DayPlanSidebar(props: DayPlanSidebarP
const timeText = [
dispTime ? formatTime(dispTime, locale, timeFormat) : '',
spanPhase === 'single' && endTime ? `– ${formatTime(endTime, locale, timeFormat)}` : '',
- meta.departure_timezone && spanPhase === 'start' ? meta.departure_timezone : '',
- meta.arrival_timezone && spanPhase === 'end' ? meta.arrival_timezone : '',
].filter(Boolean).join(' ')
- if (!timeText && !spanLabel && !transitMeta && !subtitle) return null
+ // The zone a leg's time is read in. Written out it took the width
+ // the route needs, so it is a globe that names it on hover.
+ const timeZone = (spanPhase === 'start' && meta.departure_timezone) || (spanPhase === 'end' && meta.arrival_timezone) || ''
+ if (!timeText && !timeZone && !spanLabel && !transitMeta && !subtitle) return null
return (
{/* Where in a multi-day span this day falls, said quietly beside its time. */}
{spanLabel && {spanLabel}}
{timeText && {timeText}}
+ {timeZone && (
+
+
+
+
+
+ )}
{transitMeta ? (
) : subtitle && (