diff --git a/src/features/online-board/components/TransferBar/computeTransferTime.test.ts b/src/features/online-board/components/TransferBar/computeTransferTime.test.ts new file mode 100644 index 00000000..ddc19f5d --- /dev/null +++ b/src/features/online-board/components/TransferBar/computeTransferTime.test.ts @@ -0,0 +1,47 @@ +import { describe, it, expect } from "vitest"; +import { computeTransferMinutes, formatMinutesAsDuration } from "./computeTransferTime.js"; + +describe("computeTransferMinutes", () => { + it("returns positive diff in minutes", () => { + expect(computeTransferMinutes("2026-04-17T10:00:00Z", "2026-04-17T11:30:00Z")).toBe(90); + }); + + it("returns 0 when times are identical", () => { + const t = "2026-04-17T10:00:00Z"; + expect(computeTransferMinutes(t, t)).toBe(0); + }); + + it("returns null when departure is before arrival (negative)", () => { + expect(computeTransferMinutes("2026-04-17T11:00:00Z", "2026-04-17T10:00:00Z")).toBeNull(); + }); + + it("returns null when arrival is undefined", () => { + expect(computeTransferMinutes(undefined, "2026-04-17T10:00:00Z")).toBeNull(); + }); + + it("returns null when departure is undefined", () => { + expect(computeTransferMinutes("2026-04-17T10:00:00Z", undefined)).toBeNull(); + }); + + it("returns null for invalid ISO strings", () => { + expect(computeTransferMinutes("not-a-date", "2026-04-17T10:00:00Z")).toBeNull(); + }); +}); + +describe("formatMinutesAsDuration", () => { + it("formats 90 minutes as '1h 30m'", () => { + expect(formatMinutesAsDuration(90)).toBe("1h 30m"); + }); + + it("formats 45 minutes as '45m'", () => { + expect(formatMinutesAsDuration(45)).toBe("45m"); + }); + + it("formats 60 minutes as '1h'", () => { + expect(formatMinutesAsDuration(60)).toBe("1h"); + }); + + it("formats 0 as '0m'", () => { + expect(formatMinutesAsDuration(0)).toBe("0m"); + }); +}); diff --git a/src/features/online-board/components/TransferBar/computeTransferTime.ts b/src/features/online-board/components/TransferBar/computeTransferTime.ts new file mode 100644 index 00000000..c9598cd2 --- /dev/null +++ b/src/features/online-board/components/TransferBar/computeTransferTime.ts @@ -0,0 +1,29 @@ +import { parseISO, differenceInMinutes, isValid } from "date-fns"; + +/** + * Diff (in minutes) between departure and arrival ISO UTC strings. + * Returns null when either input is missing/invalid, or when result is negative. + */ +export function computeTransferMinutes( + arrivalUtc: string | undefined, + departureUtc: string | undefined, +): number | null { + if (!arrivalUtc || !departureUtc) return null; + const arr = parseISO(arrivalUtc); + const dep = parseISO(departureUtc); + if (!isValid(arr) || !isValid(dep)) return null; + const diff = differenceInMinutes(dep, arr); + if (diff < 0) return null; + return diff; +} + +/** + * Format a minute count as "Nh Mm" (or "Mm" when <1 hour, or "Nh" when no remainder). + */ +export function formatMinutesAsDuration(minutes: number): string { + if (minutes < 60) return `${minutes}m`; + const h = Math.floor(minutes / 60); + const m = minutes % 60; + if (m === 0) return `${h}h`; + return `${h}h ${m}m`; +}