From de3b2a6ac744328ee382b99ccf14dd847500f210 Mon Sep 17 00:00:00 2001 From: Maurice Date: Sun, 13 Sep 2026 16:22:07 +0200 Subject: [PATCH] fix(roadtrip): a check-in sets the hour when nothing before it does MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Book a night to check in at ten, pin a stop to three in the afternoon behind it, and the rail said 13:46. True enough as arithmetic — with no hour of its own the chain works the first stop backwards out of the first one that has one, so three minus a fourteen minute drive minus an hour standing there — and nothing anybody asked for. A door that opens at ten, with nothing ahead of it deciding otherwise, IS the hour: the day starts there and runs forwards. Reached later it stays what it was, a floor, and a floor below the arrival changes nothing — which is what keeps a hotel behind an afternoon stop at its real arrival instead of dragging it back to its check-in. --- shared/src/roadtrip/roadtripModel.spec.ts | 14 ++++++++++++++ shared/src/roadtrip/roadtripModel.ts | 12 +++++++++--- 2 files changed, 23 insertions(+), 3 deletions(-) diff --git a/shared/src/roadtrip/roadtripModel.spec.ts b/shared/src/roadtrip/roadtripModel.spec.ts index f6bbd03b1..c7efffe43 100644 --- a/shared/src/roadtrip/roadtripModel.spec.ts +++ b/shared/src/roadtrip/roadtripModel.spec.ts @@ -865,6 +865,20 @@ describe('a check-in is a door opening, not an appointment', () => { expect(schedule.warnings).toEqual([]); }); + it('sets the hour when nothing before it does, instead of being worked backwards', () => { + // Booked to check in at ten, with a stop pinned to three in the afternoon behind it. + // Read only as a floor, the chain worked the hotel backwards out of the afternoon + // and put it at 13:46: true enough as arithmetic, and nothing anybody asked for. + const schedule = computeSchedule( + [{ anchor: null, earliest: '10:00', dwellMinutes: 60 }, { anchor: '15:00', dwellMinutes: 60 }], + [14 * 60], + ); + expect(schedule.entries[0]!.arrival).toBe('10:00'); + expect(schedule.entries[0]!.anchored).toBe(true); + expect(schedule.entries[1]!.arrival).toBe('15:00'); + expect(schedule.warnings).toEqual([]); + }); + it('is simply arrived at when the drive gets there later', () => { const schedule = computeSchedule( [{ anchor: '15:00', dwellMinutes: 60 }, { anchor: null, earliest: '11:00', dwellMinutes: 60 }], diff --git a/shared/src/roadtrip/roadtripModel.ts b/shared/src/roadtrip/roadtripModel.ts index 1d49e3416..e56dfc9ee 100644 --- a/shared/src/roadtrip/roadtripModel.ts +++ b/shared/src/roadtrip/roadtripModel.ts @@ -199,10 +199,16 @@ export function computeSchedule( // A door that opens at eleven is not an appointment at eleven. Reaching the stop // later than that is simply reaching it; only a time somebody pinned can be missed. const opens = parseClock(stop.earliest ?? null); - const waited = anchor === null && opens !== null && resolved.arrival !== null - ? opens + Math.round((resolved.arrival - opens) / DAY_MINUTES) * DAY_MINUTES + // With nothing before it deciding the hour, the door IS the hour: a night booked + // to check in at ten starts the day at ten rather than being worked backwards out + // of whatever comes after it. Reached later, it is only a floor, and a floor below + // the arrival changes nothing. + const waited = anchor === null && opens !== null + ? resolved.arrival === null + ? opens + dayOffset * DAY_MINUTES + : opens + Math.round((resolved.arrival - opens) / DAY_MINUTES) * DAY_MINUTES : null; - const held = waited !== null && waited > resolved.arrival!; + const held = waited !== null && (resolved.arrival === null || waited > resolved.arrival); const arrival = held ? waited : resolved.arrival; const lateBy = resolved.lateBy; if (lateBy !== null) warnings.push({ index: i, code: 'late', minutes: lateBy });