From 080afa9cffdf7b92201026ff279392cbead4efc9 Mon Sep 17 00:00:00 2001 From: Maurice Date: Fri, 9 Oct 2026 10:11:36 +0200 Subject: [PATCH] fix(trips): keep the trip length across a DST change when the phone moves the start The phone sheet added the old length with a local setDate on a UTC midnight date, so in zones east of UTC a range crossing the start of summer time came out one day short. It now counts UTC calendar days like the desktop dialog. --- client/src/components/Trips/tripFormModel.test.ts | 14 +++++++++++++- client/src/components/Trips/tripFormModel.ts | 11 ++++------- 2 files changed, 17 insertions(+), 8 deletions(-) diff --git a/client/src/components/Trips/tripFormModel.test.ts b/client/src/components/Trips/tripFormModel.test.ts index c21e1048c..5b1a44e85 100644 --- a/client/src/components/Trips/tripFormModel.test.ts +++ b/client/src/components/Trips/tripFormModel.test.ts @@ -1,4 +1,4 @@ -// FE-COMP-TRIPFORMMODEL-001 to -006: the trip form checks the dialog and the phone sheet share. +// FE-COMP-TRIPFORMMODEL-001 to -007: the trip form checks the dialog and the phone sheet share. import { MAX_TRIP_DAYS, addIsoDays } from '@trek/shared'; import { endDateForNewStart, tripFormError } from './tripFormModel'; @@ -52,4 +52,16 @@ describe('endDateForNewStart', () => { expect(endDateForNewStart('2026-05-08', '2026-05-03', '2026-05-05')).toBe('2026-05-05'); expect(endDateForNewStart('2026-05-01', '2026-05-08', '')).toBe('2026-05-08'); }); + + it('FE-COMP-TRIPFORMMODEL-007: the kept length counts calendar days across a DST change', () => { + const runnerTimeZone = process.env.TZ; + process.env.TZ = 'Europe/Berlin'; + try { + // Summer time starts on 2026-03-29 in Berlin. + expect(endDateForNewStart('2026-03-20', '2026-03-25', '2026-03-27')).toBe('2026-04-01'); + } finally { + if (runnerTimeZone === undefined) delete process.env.TZ; + else process.env.TZ = runnerTimeZone; + } + }); }); diff --git a/client/src/components/Trips/tripFormModel.ts b/client/src/components/Trips/tripFormModel.ts index a8db48e84..a1d38bf42 100644 --- a/client/src/components/Trips/tripFormModel.ts +++ b/client/src/components/Trips/tripFormModel.ts @@ -1,4 +1,4 @@ -import { MAX_TRIP_DAYS, tripSpanDays } from '@trek/shared'; +import { MAX_TRIP_DAYS, addIsoDays, tripSpanDays } from '@trek/shared'; type T = (key: string, params?: Record) => string; @@ -34,12 +34,9 @@ export function tripFormError( */ export function endDateForNewStart(startDate: string, endDate: string, value: string): string { if (value && endDate && startDate && endDate >= startDate) { - const duration = Math.round( - (new Date(endDate + 'T00:00:00Z').getTime() - new Date(startDate + 'T00:00:00Z').getTime()) / 86400000 - ); - const newEnd = new Date(value + 'T00:00:00Z'); - newEnd.setDate(newEnd.getDate() + duration); - return newEnd.toISOString().split('T')[0]; + // Counted in UTC calendar days: a local-time shift across a DST change used to + // drop or add a day. + return addIsoDays(value, tripSpanDays(startDate, endDate) - 1); } if (value && (!endDate || endDate < value)) return value; return endDate;