Journey: show actual transfer/layover time between legs

Replaces the generic "1x umsteigen" summary with the real gap per
transfer (e.g. "SchnellBus S20 ->13min-> Regionalzug RB 67"), computed
from each leg's estimated (falling back to planned) arrival/departure.
Falls back to a plain "umsteigen" label when EFA doesn't supply both
timestamps for a given transfer.
This commit is contained in:
Honey committed 2026-07-23 10:35:54 +02:00
1 parent 4380a62524
commit 4c10ff94d2
1 file changed
+14 -3
+14 -3
View File
@@ -275,6 +275,16 @@ class JourneyScreen(Screen):
if title and title not in notice_titles: if title and title not in notice_titles:
notice_titles.append(title) notice_titles.append(title)
# Layover between consecutive legs: next leg's (estimated,
# falling back to planned) departure minus this leg's (likewise)
# arrival. None when either side is missing a timestamp, so the
# render step can fall back to a plain "umsteigen" label.
transfer_minutes = []
for cur, nxt in zip(legs, legs[1:]):
arr = _parse_time(cur["destination"].get("arrivalTimeEstimated")) or _parse_time(cur["destination"].get("arrivalTimePlanned"))
dep = _parse_time(nxt["origin"].get("departureTimeEstimated")) or _parse_time(nxt["origin"].get("departureTimePlanned"))
transfer_minutes.append(max(0, int((dep - arr).total_seconds() // 60)) if arr and dep else None)
# EFA returns UTC timestamps (Z suffix) -- astimezone() with no # EFA returns UTC timestamps (Z suffix) -- astimezone() with no
# args converts to the system's local timezone before display. # args converts to the system's local timezone before display.
# Without this, times showed 2h behind (CEST is UTC+2), making # Without this, times showed 2h behind (CEST is UTC+2), making
@@ -285,6 +295,7 @@ class JourneyScreen(Screen):
"arrival": arr_actual.astimezone().strftime("%H:%M") if arr_actual else "--:--", "arrival": arr_actual.astimezone().strftime("%H:%M") if arr_actual else "--:--",
"legs": [{"name": leg["line"], "mode": leg["mode"]} for leg in legs], "legs": [{"name": leg["line"], "mode": leg["mode"]} for leg in legs],
"transfers": len(legs) - 1, "transfers": len(legs) - 1,
"transfer_minutes": transfer_minutes,
"platform": (first["origin"].get("properties") or {}).get("platformName") or "", "platform": (first["origin"].get("properties") or {}).get("platformName") or "",
"notice": "; ".join(notice_titles), "notice": "; ".join(notice_titles),
}) })
@@ -389,11 +400,11 @@ class JourneyScreen(Screen):
shown = draw_segment(leg["name"], avail=right_edge - cursor_x) shown = draw_segment(leg["name"], avail=right_edge - cursor_x)
if idx < len(journey["legs"]) - 1 and cursor_x < right_edge and shown == leg["name"]: if idx < len(journey["legs"]) - 1 and cursor_x < right_edge and shown == leg["name"]:
draw_segment(" → ", avail=right_edge - cursor_x) gap = journey["transfer_minutes"][idx]
sep = f" →{gap}min→ " if gap is not None else " → umsteigen → "
draw_segment(sep, avail=right_edge - cursor_x)
trailing_parts = [] trailing_parts = []
if journey["transfers"] > 0:
trailing_parts.append(f"{journey['transfers']}x umsteigen")
if journey["platform"]: if journey["platform"]:
trailing_parts.append(f"Gl. {journey['platform']}") trailing_parts.append(f"Gl. {journey['platform']}")
if trailing_parts and cursor_x < right_edge: if trailing_parts and cursor_x < right_edge: