Skip to content

Commit b66d786

Browse files
committed
MAX_HOURS_PER_DAY
1 parent e800bc4 commit b66d786

3 files changed

Lines changed: 19 additions & 17 deletions

File tree

src/controllers/journey/journeys-overview.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import { Layers } from "@/components/layers";
1515
import { tip } from "@/components/tooltips";
1616
import { Controllers } from "@/controllers";
1717
import { cellEndpointLabel, getCellPoint } from "@/controllers/journey/journey-cell-labels";
18+
import { MAX_HOURS_PER_DAY } from "@/generators/transports-generator";
1819
import { startJourneyTravel, stopJourneyTravel } from "@/renderers/journey-travel";
1920
import { highlightElement } from "@/renderers/overlays/highlight";
2021
import type { Journey } from "@/types/Journey";
@@ -199,7 +200,7 @@ function renderJourneysPage(view: TableView<Journey>): void {
199200
const totalHours = sum(totals.map(t => t.totalHours));
200201
// days are counted per transport, so the rate that reproduces them is what the total formats with
201202
const totalDays = sum(totals.map(t => t.totalDays));
202-
const footerHoursPerDay = totalDays > 0 ? totalHours / totalDays : undefined;
203+
const footerHoursPerDay = totalDays > 0 ? totalHours / totalDays : MAX_HOURS_PER_DAY;
203204
const footerTime = ensureEl("journeysFooterTime");
204205
footerTime.innerHTML = Journeys.formatTravelTime(totalHours, footerHoursPerDay);
205206
footerTime.parentElement!.dataset.tip = `Total travel time: ${Journeys.formatTravelTimeFull(totalHours, footerHoursPerDay)}. Days are counted from each transport's travel hours`;

src/generators/journeys-generator.test.ts

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -85,18 +85,18 @@ describe("journey metrics", () => {
8585
expect(Journeys.formatTravelTime(t.totalHours, t.hoursPerDay)).toBe("4d");
8686
});
8787

88-
it("formatTravelTime handles days/hours/minutes with default 8h/day", () => {
89-
expect(Journeys.formatTravelTime(0)).toBe("0m");
90-
expect(Journeys.formatTravelTime(0.5)).toBe("30m");
91-
expect(Journeys.formatTravelTime(1.5)).toBe("1h 30m");
88+
it("formatTravelTime handles days/hours/minutes", () => {
89+
expect(Journeys.formatTravelTime(0, 8)).toBe("0m");
90+
expect(Journeys.formatTravelTime(0.5, 8)).toBe("30m");
91+
expect(Journeys.formatTravelTime(1.5, 8)).toBe("1h 30m");
9292
// 25h at 8h/day = 3d 1h
93-
expect(Journeys.formatTravelTime(25)).toBe("3d 1h");
93+
expect(Journeys.formatTravelTime(25, 8)).toBe("3d 1h");
9494
});
9595

9696
it("formatTravelTime drops the smaller unit once the larger dominates", () => {
97-
expect(Journeys.formatTravelTime(2400)).toBe("300d"); // 300 travel days at 8h/day
98-
expect(Journeys.formatTravelTime(2404.15)).toBe("300d"); // the odd hours are noise
99-
expect(Journeys.formatTravelTime(75)).toBe("9d 3h"); // under 10 days, hours still matter
97+
expect(Journeys.formatTravelTime(2400, 8)).toBe("300d"); // 300 travel days at 8h/day
98+
expect(Journeys.formatTravelTime(2404.15, 8)).toBe("300d"); // the odd hours are noise
99+
expect(Journeys.formatTravelTime(75, 8)).toBe("9d 3h"); // under 10 days, hours still matter
100100
expect(Journeys.formatTravelTime(11.5, 24)).toBe("11h"); // hours-only above the threshold
101101
expect(Journeys.formatTravelTime(3.25, 24)).toBe("3h 15m"); // below it, minutes still show
102102
});
@@ -107,8 +107,8 @@ describe("journey metrics", () => {
107107
});
108108

109109
it("formatTravelTimeFull keeps every unit for the tooltip", () => {
110-
expect(Journeys.formatTravelTimeFull(2404.15)).toBe("300d 4h 9m");
111-
expect(Journeys.formatTravelTimeFull(0)).toBe("0m");
110+
expect(Journeys.formatTravelTimeFull(2404.15, 8)).toBe("300d 4h 9m");
111+
expect(Journeys.formatTravelTimeFull(0, 8)).toBe("0m");
112112
expect(Journeys.formatTravelTimeFull(25, 24)).toBe("1d 1h");
113113
});
114114

src/generators/journeys-generator.ts

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,8 @@ import { getCardinalColor } from "../utils/colorUtils";
55
import type { Burg } from "./burgs-generator";
66
import { generateStoryJourney } from "./journey-story";
77
import type { Route } from "./routes-generator";
8-
import type { TransportDomain } from "./transports-generator";
8+
import { MAX_HOURS_PER_DAY, type TransportDomain } from "./transports-generator";
99

10-
const DEFAULT_HOURS_PER_DAY = 8;
1110
const COARSE_UNIT_THRESHOLD = 10;
1211
const ON_ROAD_DISCOUNT = 0.5;
1312
const OFF_ROAD_PENALTY = 5;
@@ -110,12 +109,13 @@ class JourneysModule {
110109

111110
// avgSpeed is km/h like every other stored speed, so the distance-unit ratio has to come back out
112111
const avgSpeed = movingHours > 0 ? totalDistance / movingHours / getDistanceUnitRatio() : 0;
113-
const hoursPerDay = totalDays > 0 ? totalHours / totalDays : DEFAULT_HOURS_PER_DAY;
112+
// the rate that reproduces the per-transport day count; with nothing to divide, any rate formats "0m"
113+
const hoursPerDay = totalDays > 0 ? totalHours / totalDays : MAX_HOURS_PER_DAY;
114114
return { totalDistance, totalHours, avgSpeed, totalDays, hoursPerDay };
115115
}
116116

117117
/** Readable duration, e.g. "2d 3h". Days are counted from `hoursPerDay` */
118-
formatTravelTime(hours: number, hoursPerDay = DEFAULT_HOURS_PER_DAY): string {
118+
formatTravelTime(hours: number, hoursPerDay: number): string {
119119
const { days, hours: restHours, minutes } = this.splitTravelTime(hours, hoursPerDay);
120120

121121
if (days >= COARSE_UNIT_THRESHOLD) return `${days}d`;
@@ -126,7 +126,7 @@ class JourneysModule {
126126
}
127127

128128
/** Exact duration down to the minute, e.g. "52d 4h 9m" — for tooltips */
129-
formatTravelTimeFull(hours: number, hoursPerDay = DEFAULT_HOURS_PER_DAY): string {
129+
formatTravelTimeFull(hours: number, hoursPerDay: number): string {
130130
const { days, hours: restHours, minutes } = this.splitTravelTime(hours, hoursPerDay);
131131

132132
const parts: string[] = [];
@@ -137,7 +137,8 @@ class JourneysModule {
137137
}
138138

139139
private splitTravelTime(hours: number, hoursPerDay: number): { days: number; hours: number; minutes: number } {
140-
const minutesPerDay = (hoursPerDay > 0 ? hoursPerDay : DEFAULT_HOURS_PER_DAY) * 60;
140+
// a corrupt rate must not inflate the day count, so fall back to the longest possible day
141+
const minutesPerDay = (hoursPerDay > 0 ? hoursPerDay : MAX_HOURS_PER_DAY) * 60;
141142
const totalMinutes = Number.isFinite(hours) && hours > 0 ? Math.round(hours * 60) : 0;
142143
let days = Math.floor(totalMinutes / minutesPerDay);
143144
// a journey mixing transports has a fractional rate, so the remainder needs rounding to whole minutes

0 commit comments

Comments
 (0)