Skip to content

Demurrage Clock

  • Container free time, demurrage and detention clocks, billing deadlines, bill of lading dates, multimodal journeys, EPCIS events and EDI timestamps.

A container’s charges run on clocks, and each clock runs between two of its events. Demurrage runs while the container is inside the terminal. Detention runs while it is outside. Storage is the charge for the terminal’s space, and the merged clock runs demurrage and detention as one period.

List the events, choose the leg and the clock, and the tool calls demurrageClock once for each of the four clocks. The chosen clock is lit and the other three are dimmed. A clock the library cannot read keeps its row and says why in words. The tool does not hold a table of which events go with which clock: it asks the library, with one event of each type, and reads the pair back.

1. Set the events and the options

An import container is discharged, becomes available, leaves the terminal and comes back empty. The demurrage clock runs from discharge to gate-out.

The terminal clock is a display zone. It sets the day columns and the local times. It is not an argument of demurrageClock, so it never changes an interval.

Events

2. Which events bound each clock

demurrage
detention
storage
combined
  • event
  • chosen clock
  • other clocks, dimmed

3. What demurrageClock returns

 

The rule. The same clock runs between different events on the import leg and the export leg, so the direction has no default. The library does not guess it from the events. An event recorded twice is a data error and not a choice, so every clock that needs it returns null. A clock that ends before it starts returns null too. The result is half-open: it starts at the first event and stops just before the second.

The terminal clock is a display zone, not an argument. The zone you pick sets the day columns and the local times on the chips and plates. It is not passed to demurrageClock, which returns your own strings unchanged. Change the zone and the days move, but the intervals do not.

const events = [
{ type: "discharged", at: "2024-06-14T19:00:00Z" },
{ type: "available", at: "2024-06-15T12:00:00Z" },
{ type: "gatedOut", at: "2024-06-20T14:30:00Z" },
{ type: "emptyReturned", at: "2024-06-27T09:00:00Z" },
];
demurrageClock(events, "demurrage", { direction: "import" }); // { start: "2024-06-14T19:00:00Z", end: "2024-06-20T14:30:00Z" }
demurrageClock(events, "detention", { direction: "import" }); // { start: "2024-06-20T14:30:00Z", end: "2024-06-27T09:00:00Z" }
demurrageClock(events, "storage", { direction: "import" }); // { start: "2024-06-14T19:00:00Z", end: "2024-06-20T14:30:00Z" }
demurrageClock(events, "combined", { direction: "import" }); // { start: "2024-06-14T19:00:00Z", end: "2024-06-27T09:00:00Z" }

How long it ran. demurrageClock returns the strings you gave it, and diffUtcAsDuration reads Z instants only. So the tool passes each end through toOffsetInstant first, then measures between the two. The demurrage example above ran for PT139H30M, which the plate shows as 5 d 19 h 30 min.

Count the days. The link under the chart sends the chosen clock, as the library returned it, to the Free Time Ledger with the terminal clock. There you set the free days and see which days are charged. The rules are in Intermodal: Free Time and Demurrage.

Worth trying: the start event set to available — the demurrage clock now starts at the availability date, 2024-06-15T12:00:00Z, and ends at the same gate-out. Then the same four events read as an export — the result is NO SIGNAL, because an export demurrage clock runs from the full gate-in to the loading, and neither is recorded. For a clock that returns a result on the export leg, try the merged export clock — it runs from the empty pick-up to the loading, 2024-07-01T08:00:00Z to 2024-07-09T03:00:00Z.

Reference: demurrageClock picks the clock, toOffsetInstant puts an instant on one clock, and diffUtcAsDuration measures between two Z instants.