Skip to content

intervalOverlappingDaysZoned

intervalOverlappingDaysZoned(aStart: string, aEnd: string, bStart: string, bEnd: string): number
import { intervalOverlappingDaysZoned } from "@northguild/gmt/zoned/interval";

Return how many distinct calendar dates two zoned intervals share, counted in the

  • first interval’s zone.
  • Counts the number of local dates touched by the closed intersection [max(aStart, bStart), min(aEnd, bEnd)] — inclusive of both endpoints.
  • aEnd, bStart, and bEnd are re-expressed in aStart’s time zone before comparing — Temporal refuses to compute day-granularity differences directly across two zones, since day length varies with DST/offset changes. As a result this function is NOT commutative: swapping the two intervals can change the answer when their zones differ.
  • Zones whose calendar skips a date entirely (e.g. Pacific/Apia’s 2011 dateline change) still count the skipped date — this matches intervalCountZoned’s calendar-arithmetic rule, so the two functions never disagree about what a day is.
  • Adjacent intervals (e.g. aEnd === bStart) share one date and count as 1.
  • Returns 0 when the intervals do not overlap at all (a well-defined answer, not invalid input).
  • Returns null if either interval is invalid (start > end).
  • Returns null on invalid input (wrong type, malformed strings, leap seconds).
  • Accepts mixed calendar systems (E7’s D4-zoned, issue #152): both bare ISO zoned strings and GMT calendar-annotated ones ("5784-06-15T14:30:00-05:00[u-ca=hebrew][America/New_York]"), and the two endpoints need not agree on a calendar. Ordering is calendar-independent — verified that Temporal.Instant carries no calendar field at all and that Instant.compare/ZonedDateTime.compare both return 0 for the same instant expressed in hebrew, islamic-civil, japanese and iso8601.
  • Still rejects Temporal’s own [timeZone][u-ca=...] RFC 9557 ordering, which reads GMT’s calendar-native digits as ISO digits — see regex/calendar-zoned-date-time.ts.
  • Diverges from date-fns’s getOverlappingDaysInIntervals, which rounds up elapsed 24-hour periods instead of counting calendar dates. To reproduce date-fns’s number, compose intervalIntersectionZoned with intervalCountZoned: const span = intervalIntersectionZoned(aStart, aEnd, bStart, bEnd); span ? intervalCountZoned(span.start, span.end, "day") : 0;
Parameter Type Description
aStart string ISO 8601 zoned datetime string for the first interval start
aEnd string ISO 8601 zoned datetime string for the first interval end
bStart string ISO 8601 zoned datetime string for the second interval start
bEnd string ISO 8601 zoned datetime string for the second interval end

number of shared calendar dates (counted in aStart’s zone), 0 when disjoint, or null on invalid input

intervalOverlappingDaysZoned("2024-03-09T12:00:00-05:00[America/New_York]", "2024-03-11T12:00:00-04:00[America/New_York]", "2024-03-09T12:00:00-05:00[America/New_York]", "2024-03-11T12:00:00-04:00[America/New_York]") // 3 (spring-forward, 47 real hours)
intervalOverlappingDaysZoned("2024-01-01T00:00:00+00:00[UTC]", "2024-01-02T00:00:00+00:00[UTC]", "2024-01-03T00:00:00+00:00[UTC]", "2024-01-04T00:00:00+00:00[UTC]") // 0 (disjoint)
intervalOverlappingDaysZoned("invalid", "2024-06-30T23:59:59+00:00[UTC]", "2024-04-01T00:00:00+00:00[UTC]", "2024-12-31T23:59:59+00:00[UTC]") // null

zoned/interval/intervalOverlappingDaysZoned.ts