Skip to content

billingTimeline

  • Container free time, demurrage, billing deadlines, bill of lading dates and EDI timestamps.
billingTimeline(dates: BillingDates, windows: BillingWindows): BillingDeadlines | null

Lay out the deadline chain around a demurrage or detention invoice: the last date it may be issued, the last date it may be disputed, and the last date the dispute must be resolved.

  • A billing regime or service contract can set up to three windows around an invoice: one to issue it, one to dispute it, and one to resolve the dispute. The number of days in each is the caller’s fact; GMT carries none of them. The arithmetic is this function’s: each deadline is a date, counted in calendar days from a date, with the anchor as day zero and the deadline day itself inside the window.
  • Day zero is the anchor and the deadline is anchor + days on the ISO 8601 calendar (Temporal.PlainDate.add); a date is “by the deadline” when Temporal.PlainDate.compare(date, deadline) <= 0. That is this function’s stated contract, not a reading of any rule; a regime that counts differently passes a different number.
  • Deadlines are dates, never instants. Reduce an instant to the billing party’s local date first with convertUtcToPlainDate(instant, { timeZone }); this function does not guess a zone. anchorOn is whatever date the caller counts from, such as the last charged date (chargeableDays(...).chargedDates.at(-1)) or, for a party re-billing a charge it was itself billed, the issuance date of the invoice it received.
  • Windows have no defaults. Each is a safe integer of at least 0; a missing, negative or non-integer window returns null, because a silently defaulted window is a wrong deadline.
  • The chain fills in as its dates exist. With only anchorOn the result is a forecast: invoiceDeadline is set and every other field is null. An invoice date sets issuedByDeadline and disputeDeadline; a request date sets requestedByDeadline and resolutionDeadline. An explicit undefined is an omission; any other value must be a valid ISO date.
  • The booleans compare dates and say nothing else. Whether a charge is payable, whether a dispute must be heard, and any consequence are the consumer’s. GMT computes dates, not liability.
  • agreedResolutionOn replaces the computed resolutionDeadline when given. It must be a valid date on or after requestReceivedOn, else null; it is validated even when no request exists, but cannot act without one.
  • A request cannot precede the invoice it disputes. requestReceivedOn without invoiceIssuedOn, or earlier than it, is a data error and returns null. An invoice earlier than the anchor is allowed: an invoice may be issued while charges still accrue.
  • Every emitted date is canonical bare ISO (Temporal.PlainDate#toString()), so an input annotation such as [u-ca=iso8601] does not reach the output, as addDate behaves.
  • Returns null when dates or windows is not an object, anchorOn or a given optional date is not a valid ISO date, a window is missing or not a safe integer of at least 0, a request exists without an invoice or before it, agreedResolutionOn is before the request, or a deadline would leave Temporal’s range.
ParameterTypeDescription
datesBillingDatesThe anchor date, and the invoice and request dates once they exist
windowsBillingWindowsThe three calendar-day windows, none defaulted, and an agreed resolution date

{ invoiceDeadline, issuedByDeadline, disputeDeadline, requestedByDeadline, resolutionDeadline }, or null on invalid input

The dates a billing timeline is counted from: the anchor, and the invoice and request once they exist.

import type { BillingDates } from "@northguild/gmt";
MemberTypeDescription
anchorOnstringThe date the issue window is counted from, as an ISO date, such as the date the charge was last incurred or, for a re-bill, the issuance date of the invoice received.
invoiceIssuedOn?stringThe date the invoice was issued, once it has been, as an ISO date.
requestReceivedOn?stringThe date the dispute request was received, once it has been, as an ISO date. It needs invoiceIssuedOn and may not precede it.

The three windows the caller supplies, in calendar days, none defaulted, and an optional agreed resolution date.

import type { BillingWindows } from "@northguild/gmt";
MemberTypeDescription
issueDaysnumberThe days from anchorOn within which the invoice must be issued.
disputeDaysnumberThe days from invoiceIssuedOn within which a dispute may be raised.
resolutionDaysnumberThe days from requestReceivedOn within which the dispute must be resolved.
agreedResolutionOn?stringA resolution date the parties agreed instead, as an ISO date on or after requestReceivedOn; it replaces the computed one.

What billingTimeline returns: each deadline as an ISO date, and each comparison once its date exists.

import type { BillingDeadlines } from "@northguild/gmt";
MemberTypeDescription
invoiceDeadlinestringThe last date the invoice may be issued: anchorOn + issueDays.
issuedByDeadlineboolean | nullWhether invoiceIssuedOn is on or before invoiceDeadline; null until an invoice date exists.
disputeDeadlinestring | nullThe last date a dispute may be raised: invoiceIssuedOn + disputeDays; null until an invoice date exists.
requestedByDeadlineboolean | nullWhether requestReceivedOn is on or before disputeDeadline; null until a request date exists.
resolutionDeadlinestring | nullThe last date the dispute must be resolved: requestReceivedOn + resolutionDays, or agreedResolutionOn; null until a request date exists.
import { billingTimeline } from "@northguild/gmt/intermodal/calculate";
billingTimeline({ anchorOn: "2026-03-01" }, { issueDays: 30, disputeDays: 30, resolutionDays: 30 }) // { invoiceDeadline: "2026-03-31", issuedByDeadline: null, disputeDeadline: null, requestedByDeadline: null, resolutionDeadline: null } (no invoice yet: a forecast)
output 

intermodal/calculate/billingTimeline.ts