Skip to content

dwellTime

  • Legs, dwell, cut-offs and punctuality, shared by road, rail, sea and air.
dwellTime(entry: string, exit: string, targetZone?: string): Dwell | null

Measure how long something sat somewhere, and how many local calendar days it sat there.

  • Terminals, depots, yards, stations, gates and berths all charge dwell, and none of them charge by elapsed hours. They count the local calendar days the thing was present, so a container that gates in at 23:00 and out at 01:00 has been there two hours and two days. dwellTime returns both, and is the one “local days crossed” primitive in the library: free time and demurrage (INT-12), laytime day counts (MAR-19) and hospital length of stay all take their day boundary from here rather than each deciding what a midnight is.
  • duration is exact elapsed time. A dwell spanning a DST transition reports the hours that actually elapsed; the local wall clocks at either end can be an hour apart from that. Hours is the largest unit, so the value never depends on a calendar.
  • calendarDays is not duration rounded up. It is the number of distinct local dates in targetZone that the half-open interval [entry, exit) touches: entry and exit on the same local date is 1, across one local midnight is 2, and so on. A dwell that ends exactly at a local midnight does not touch the new day, because the exit instant itself is excluded — the same half-open rule as bucketRange and CORE-6’s intervals. A zero-length dwell touches one date. The count walks the zone’s real transitions: between two of them the wall clock runs forward without a break, so each stretch touches every date from its first instant to its last, and the count is the size of the union of those dates. A 23- or 25-hour local day is one day, not 0.96 or 1.04 of one; a date the zone deleted is never touched (Pacific/Apia skipped 2011-12-30); and a date the clock falls back into is still one date (America/Goose_Bay fell back from 00:01 on 7 November 2010 into the 6th).
  • The zone decides the count, so it has to be stated. targetZone is used when given. Without it, the entry’s bracketed IANA zone is used. A bracketed zone is otherwise not validated: the entry’s must exist and agree with its offset only when it supplies the dwell zone, because targetZone is omitted, and the exit’s never supplies it. If the entry and exit are bare instants — Z or an offset — and no targetZone is given, the result is null: an offset is not a zone (CORE-4), and a day count in an unstated locality would be a guess.
  • The offset fixes each instant; a bracketed zone only resolves a rounded one. An offset written to the minute that is the bracketed zone’s sub-minute offset rounded (-00:45[Africa/Monrovia], for −00:44:30) names the instant the zone gives, as Temporal.ZonedDateTime.from reads it, on the entry and the exit alike. A zone that does not exist, or that disagrees with the offset, changes nothing.
  • enter and exit are the two instants rendered in the dwell zone, so a charging record shows the local times the terminal’s own clock showed.
  • targetZone is whatever isValidTimeZone accepts, which is what Temporal accepts: an IANA identifier, or a fixed offset such as "+02:00", as etaAtZone also takes. Days are then counted in that offset, which observes no DST, so pass the place’s IANA zone when there is one.
  • GMT does not resolve a port, terminal or station code to a zone; the caller supplies the identifier.
  • Long dwells stay cheap: the walk visits zone transitions, not days. A dwell that crosses more than 10,000 transitions (about 5,000 years of twice-yearly DST) returns null.
  • Returns null when either instant is invalid, when exit is before entry (an inverted dwell is a data error, not a negative stay), or when no zone can be determined.
  • An offset with seconds (-00:44:30) returns null. A written zone cannot carry seconds (RFC 9557 §4.1), so the result could not name it. Pass the IANA name, or call fromOffsetInstant({ instant, offset }) for the local time with its full offset and no bracket.
ParameterTypeDescription
entrystringISO 8601 zoned datetime or instant string of the gate-in, arrival or admission
exitstringISO 8601 zoned datetime or instant string of the gate-out, departure or discharge
targetZonestringIANA name or a UTC offset to the minute (what isValidTimeZone accepts) the days are counted in; defaults to entry’s bracketed zone

exact duration, zone-local entry and exit, and the local calendar days touched, or null on invalid input

What dwellTime returns: exact elapsed time plus the local view every charging regime counts in.

import type { Dwell } from "@northguild/gmt";
MemberTypeDescription
durationstringThe exact elapsed time from entry to exit, as an ISO 8601 duration with hours as the largest unit.
enterstringThe entry, rendered in the dwell zone.
exitstringThe exit, rendered in the dwell zone.
calendarDaysnumberThe number of distinct local calendar dates the dwell touched, counted half-open at the exit.
import { dwellTime } from "@northguild/gmt/transport/calculate";
dwellTime("2024-06-15T23:00:00-04:00[America/New_York]", "2024-06-16T01:00:00-04:00[America/New_York]") // { duration: "PT2H", enter: "2024-06-15T23:00:00-04:00[America/New_York]", exit: "2024-06-16T01:00:00-04:00[America/New_York]", calendarDays: 2 }
output 

transport/calculate/dwellTime.ts