Skip to content

fix(schedule): keep night routes on the board across midnight - #497

Open
ciotlosm wants to merge 3 commits into
mainfrom
fix/service-day-night-routes
Open

ciotlosm wants to merge 3 commits into
mainfrom
fix/service-day-night-routes

Conversation

@ciotlosm

@ciotlosm ciotlosm commented Oct 24, 2026 •

Copy link
Copy Markdown
Member

Between midnight and the end of the night service, the buses that were actually running vanished from the station board - and the ETA arithmetic came out ~24 h out.

The cause

A GTFS service day is not a calendar day. It starts at 00:00 and can run past midnight: a trip published at "25:10" departs at 01:10 the next morning and belongs to the previous calendar date's service day. Feeds that run night services publish exactly that - the Cluj feed's 25N / 5N / 54N lines use 24:00-25:33.

The app resolved "what is running now" against the calendar date, so during the post-midnight window the service day in progress was the wrong one and the still-running night buses were dropped from the board at exactly the moment they started running. The same class of error made every ETA computed against a schedule time from the other timeline read ~24 h out.

Measured on the real published feed, at Monday 00:30 the bus that departs at 01:10 was simply absent; with the fix it reads "in 40 min".

The fix

New module src/lib/domain/pipeline/serviceDay.ts is the single place that answers "which service days are in progress, and where do their trip times land".

Between local midnight and the rollover hour (04:00 - the convention already assumed in config.ts) two service days are in progress at once, so the station board, the reconciler's active-trip set, the map view, the favourites "active routes" set and the schedule grid all query both of them and shift the previous day's times by -1440 onto the shared timeline.

now itself stays where it was - minutes since local midnight - so every other consumer is unaffected.

The live side needed one more thing: a GTFS-RT observation carries the trip's service date, and for a night trip that is the previous date, so the orphan ETA seed now uses it instead of assuming the observation belongs to the current service day.

Also fixes the station board wiping the ETA the reconciler had already seeded onto an orphan row.

Verification

tests/night-routes.spec.ts drives the real published feed in a browser with an explicit clock, and fails against the old code with:

the 25N bus 15_0_D_4_2510 is missing from the board at 00:30 ...

A control asserts the same bus is gone once its service day is over. Plus a unit test for the service-day model itself.

pnpm check -> 0 errors, pnpm test -> 396 passed, pnpm build -> OK (verified on this branch in isolation).

Between midnight and the end of the night service, the buses that were
actually running vanished from the station board - and the ETA
arithmetic came out ~24 h out.

A GTFS service day is not a calendar day. It starts at 00:00 and can
run past midnight: a trip published at "25:10" departs at 01:10 the
next morning and belongs to the PREVIOUS calendar date's service day.
Feeds that run night services publish exactly that - the Cluj feed's
25N / 5N / 54N lines use 24:00-25:33.

The app resolved "what is running now" against the calendar date, so
during the post-midnight window the service day in progress was the
wrong one and the still-running night buses were dropped from the
board at exactly the moment they started running. The same class of
error made every ETA computed against a schedule time from the other
timeline read ~24 h out.

New module src/lib/domain/pipeline/serviceDay.ts is the single place
that answers "which service days are in progress, and where do their
trip times land". Between local midnight and the rollover hour (04:00,
the convention already assumed in config.ts) two service days are in
progress at once, so the station board, the reconciler's active-trip
set, the map view, the favourites "active routes" set and the schedule
grid all query both of them and shift the previous day's times by
-1440 onto the shared timeline. "Now" itself stays where it was -
minutes since local midnight - so every other consumer is unaffected.

The live side needed one more thing: a GTFS-RT observation carries the
trip's service date, and for a night trip that is the previous date,
so the orphan ETA seed now uses it instead of assuming the observation
belongs to the current service day.

Also fixes the station board wiping the ETA the reconciler had already
seeded onto an orphan row.

tests/night-routes.spec.ts drives the real published feed in a browser
and fails against the old code with "the 25N bus 15_0_D_4_2510 is
missing from the board at 00:30"; with the fix that bus reads "in 40
min" at 00:30, and a control asserts it is gone once its service day
is over.
@ciotlosm
ciotlosm force-pushed the fix/service-day-night-routes branch from a2f4a0c to 98a1adc Compare October 24, 2026 18:41
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant