freeTimeExpiry
Signature
Section titled “Signature”freeTimeExpiry(clockStart: string, freeDays: number, options: FreeTimeOptions): FreeTime | nullLay out a container’s free time from the instant its clock started, and say when it ends.
- Free time is the money calculation in container logistics, and none of its terms is a fact about the port: the number of free days, whether the day of discharge is free day one, and whether weekends count are all set by the carrier’s tariff or the service contract. So every term is a parameter. No regulation or industry standard fixes how free time is counted (DCSA types only its unit: calendar days, working days, hours or day of discharge; GMT counts days); the result is the window every tariff states, and what an invoice can print about it: the free time allowed and the dates it starts and ends.
- Days are the terminal’s local days.
clockStartis an instant (Z, an offset, or a bracketed zone, of which only the instant is read, a minute-rounded offset as the zone’s real one, asisValidInstantdescribes); its local date inoptions.timeZoneis the event day. A container discharged at 23:00 has used a free day by 00:01 underfirstDay: "eventDay". Day boundaries are the zone’s real ones, asfloorToZoneanddwellTimefind them: a 23- or 25-hour day is one day, a date the zone deleted is never a free day, and a fall-back that re-enters an earlier date never moves the count backwards. firstDayhas no default."eventDay"makes the event day free day one;"nextDay"starts free time on the following counted day. The two differ by exactly one day of charges, and a silent default here is a wrong invoice, so omitting it returnsnull.basisdecides which days count."calendar"counts every local day, so a weekend burns two free days while the terminal is shut."working"counts only the working days ofoptions.calendar(its weekend and holidays;calendar.timeZoneis not read), and is required to have one: a working-day count without a calendar is a guess and returnsnull. Under"working"a Saturday discharge starts free time on Monday under eitherfirstDay. A tariff counting “calendar days excluding bank holidays” (Hapag-Lloyd Japan, MSC Vietnam) is"working"with an emptyweekend. How days after expiry are charged is a separate term; seechargeableDays’schargeBasis.expiresAtis half-open. It is the first instant of the local day afterlastFreeDay, as a UTC instant. A gate-out at exactlyexpiresAtis not a chargeable day; seechargeableDays. Under"working"it can fall on a weekend, which is then simply not chargeable.freeDaysis a whole number of at least1: with no free days there is no last free day to name, andchargeableDaysaccepts0for that tariff.- Returns
nullwhenclockStartis not an instant,freeDaysis not a whole number of at least1,optionsis not an object,basisorfirstDayis missing or unknown,timeZoneis invalid,basisis"working"without a validBusinessCalendar, or the walk would pass 10,000 local days. - Limit at an offset with seconds. The instant is placed in the offset’s whole-minute zone, moved by its seconds, and the moved instant must be inside Temporal’s range. So within the offset’s seconds (under a minute) of the last instant Temporal supports (
+275760-09-13T00:00:00Z) for an offset east of UTC, or of the first (-271821-04-20T00:00:00Z) for one west, this returnsnull. An offset to the minute has no such limit.
Parameters
Section titled “Parameters”| Parameter | Type | Description |
|---|---|---|
clockStart | string | ISO 8601 instant string of the event that starts the clock (discharge, availability or gate-out) |
freeDays | number | whole number of free days the tariff allows, at least 1 |
Options
Section titled “Options”options
The tariff’s counting terms: which days count, in which zone, and where day one starts
Type: FreeTimeOptions.
| Option | Type | Default | Description |
|---|---|---|---|
basis | "calendar" | "working" (FreeTimeBasis) | Required | Which days use up free time. "calendar" counts every local day, so a weekend burns two free days while the terminal is shut; "working" counts only the days calendar marks as working, and the clock does not run on weekends or holidays. |
timeZone | string | Required | The time zone of the terminal, depot or yard whose local midnight ends a day, an IANA name or UTC offset: a time zone identifier (+05:30, +0530, -08) or a stored offset (±HH:MM[:SS], what getTimeZoneOffset returns). GMT does not resolve a port or terminal code to a zone. A UTC offset such as "+02:00" observes no DST. |
firstDay | "eventDay" | "nextDay" (FreeTimeFirstDay) | Required | Where free time starts. "eventDay" makes the day of the event that starts the clock free day one; "nextDay" starts free time on the following counted day. |
calendar? | BusinessCalendar | None. A "working" basis without it is invalid input. | The terminal’s working week and holidays. It is required when a counting basis is "working" and not read otherwise. Its own timeZone is not used: timeZone above is the counting zone. |
Returns
Section titled “Returns”the free-time start and last free day as local dates, and the expiry instant, or null on invalid input
FreeTime
Section titled “FreeTime”What freeTimeExpiry returns: the free-time window as local dates, and the instant it ends.
import type { FreeTime } from "@northguild/gmt";| Member | Type | Description |
|---|---|---|
freeTimeStart | string | The first free day, as a local date in the counting zone. |
lastFreeDay | string | The last free day, as a local date in the counting zone. |
expiresAt | string | The instant free time ends: the start of the local day after lastFreeDay, as a UTC instant. |
Related types
Section titled “Related types”Examples
Section titled “Examples”import { freeTimeExpiry } from "@northguild/gmt/intermodal/calculate";
freeTimeExpiry("2024-06-14T19:00:00Z", 3, { basis: "calendar", timeZone: "America/New_York", firstDay: "eventDay" }) // { freeTimeStart: "2024-06-14", lastFreeDay: "2024-06-16", expiresAt: "2024-06-17T04:00:00Z" } (a Friday discharge: the weekend is consumed)