Skip to content

A Connection Missed at the Spring-Forward Handoff

  • Legs, dwell, cut-offs and punctuality, shared by road, rail, sea and air.

A Connection Missed at the Spring-Forward Handoff

A truck leaves Chicago at 08:00 on 8 March 2024 on a 46-hour run to the Los Angeles port. Handling there takes two hours. The ship to Tokyo is published to sail at 06:30 on 10 March. Is the connection made?

The naive approach

// The naive approach: one UTC offset per place, from a table
const OFFSET_HOURS = { "America/Los_Angeles": -8 }; // Los Angeles, as the table was built in winter
const departed = Date.parse("2024-03-08T08:00:00-06:00"); // Chicago, 08:00
const arrived = departed + 46 * 36e5; // 2024-03-10T12:00:00.000Z
const toLocal = (ms) => new Date(ms + OFFSET_HOURS["America/Los_Angeles"] * 36e5).toISOString().slice(0, 16);
toLocal(arrived); // "2024-03-10T04:00"
const ready = arrived + 2 * 36e5; // two hours of handling: "2024-03-10T06:00" local
const sails = Date.parse("2024-03-10T06:30:00Z") - OFFSET_HOURS["America/Los_Angeles"] * 36e5; // 06:30 local, by the table
ready <= sails; // true — "made, with 30 minutes to spare"

Watch it break

The naive approach silently produces wrong results — there is no GMT equivalent to demonstrate because the failure is not using GMT.

Why

A fixed offset is a place's offset at one moment. The table's −08:00 was Los Angeles's offset when the truck left. Clocks sprang forward at 02:00 on 10 March, and the truck arrived at 12:00Z, which is 05:00 PDT. The naive code reads the arrival and the ship's published 06:30 through that same stale offset, so the handoff looks 30 minutes early. In fact the cargo is ready at 07:00, and the ship sailed at 06:30. `scheduleDelivery` keeps every handoff an exact instant. It reads the ship's published time in the port's own zone. It treats a departure before the arrival plus the handling time as a missed connection, and returns `null` rather than a negative wait.

The gmt approach

import { scheduleDelivery } from "@northguild/gmt/transport";

const truck = { departure: "2024-03-08T08:00:00-06:00[America/Chicago]", duration: "PT46H", timeZone: "America/Los_Angeles", dwellAfter: "PT2H", mode: "truck" };
const ship = { departure: "2024-03-10T06:30:00[America/Los_Angeles]", duration: "P11D", timeZone: "Asia/Tokyo", mode: "ship" };
const laterShip = { departure: "2024-03-10T07:30:00[America/Los_Angeles]", duration: "P11D", timeZone: "Asia/Tokyo", mode: "ship" };

scheduleDelivery([truck]);
// { eta: "2024-03-10T05:00:00-07:00[America/Los_Angeles]", legTimes: [{ arrival: "2024-03-10T12:00:00Z", localArrival: "2024-03-10T05:00:00-07:00[America/Los_Angeles]", dwellAfter: "PT2H", mode: "truck" }] } — 05:00, not 04:00

scheduleDelivery([truck, ship]);
// null — the ship sails inside the two-hour handoff: a missed connection

scheduleDelivery([truck, laterShip]);
// { eta: "2024-03-21T23:30:00+09:00[Asia/Tokyo]",
//   legTimes: [{ arrival: "2024-03-10T12:00:00Z", localArrival: "2024-03-10T05:00:00-07:00[America/Los_Angeles]", dwellAfter: "PT2H", mode: "truck" },
//     { arrival: "2024-03-21T14:30:00Z", localArrival: "2024-03-21T23:30:00+09:00[Asia/Tokyo]", dwellAfter: "PT0S", mode: "ship" }] } — a half hour later, the connection holds

Same widget, working

legs
output 

Open the handoff in the Connection Checker and slide the handling time down: at 90 minutes the ship sails exactly when the cargo is ready, and equal passes. At 91 it is missed. Or open the whole journey in the Delivery Scheduler and change the ship’s departure to 07:30. The full rules are in Transport: Multi-leg Scheduling.