Skip to content

intervalXorZoned

intervalXorZoned(aStart: string, aEnd: string, bStart: string, bEnd: string): { start: string; end: string; }[]

Return the symmetric difference of two half-open zoned intervals [start, end) — the instants covered by exactly one interval.

  • Compares instants (Temporal.ZonedDateTime.compare, calendar- and zone-blind).
  • The result is every maximal run covered by exactly one interval, sorted by start instant — the same as mergeIntervals of subtractIntervals(a, [b]) and subtractIntervals(b, [a]), and the same as intervalXorAllZoned([a, b]).
  • Every boundary is an input’s own start or end, in that input’s time zone (the first interval’s on a tie); no boundary is stepped by a nanosecond.
  • Touching intervals share no instant, so they join into one run. An empty interval (start and end the same instant) holds no instant and changes nothing.
  • Returns [] when the intervals are identical.
  • Returns [{ start, end }] when the intervals share a start or an end, or touch.
  • Returns [{ start, end }, { start, end }] when they partially overlap, when one strictly contains the other (the piece before B and the piece after B), or when they are disjoint with a gap.
  • Returns [] if either interval is invalid (start > end).
  • Returns [] on invalid input (wrong type, malformed strings, leap seconds).
  • Accepts RFC 9557 calendar-annotated zoned strings (as produced by convertZonedToCalendar) as well as bare ISO ones — E7 (issue #152) — but rejects a mismatched pair: every endpoint must name the same calendar system (E7’s D4-zoned). Unlike the ordering functions, this one returns a value the caller reads back as a datetime, and there is no principled way to pick one endpoint’s calendar as the answer’s. Rejection also keeps a uniform policy across all eight value-returning zoned set operations, four of which return arrays — a per-element “winner’s tag” would produce a result set whose members disagree about which calendar they are in. (intervalUnionZoned’s existing “winning endpoint’s time zone wins” is not precedent: the zone is a property of the surviving point, the calendar is a property of the answer.) A mismatch returns the sentinel.
  • Output boundaries are re-derived in the resolved calendar via formatZonedInCalendar, never copied from an input string (E7’s D7-zoned).
  • Rejects a calendar annotation before the time zone annotation, which is not RFC 9557.
ParameterTypeDescription
aStartstringISO 8601 zoned datetime string for the first interval start
aEndstringISO 8601 zoned datetime string for the first interval end
bStartstringISO 8601 zoned datetime string for the second interval start
bEndstringISO 8601 zoned datetime string for the second interval end

array of { start, end } records representing the symmetric difference, or [] on invalid input

Members of each item

MemberTypeDescription
startstringThe instant the interval begins at, as a zoned ISO 8601 string with offset and bracketed time zone. It keeps a calendar annotation such as [u-ca=hebrew] when the inputs carry one.
endstringThe first instant after the interval, in the same format as start. It is exclusive: the interval holds everything from start up to but not including this value. It is always later than start.
import { intervalXorZoned } from "@northguild/gmt/zoned/interval";
intervalXorZoned("2024-01-01T09:00:00+00:00[UTC]", "2024-06-30T12:00:00+00:00[UTC]", "2024-04-01T11:00:00+00:00[UTC]", "2024-12-31T17:00:00+00:00[UTC]") // partial overlap — [{ start: "2024-01-01T09:00:00+00:00[UTC]", end: "2024-04-01T11:00:00+00:00[UTC]" }, { start: "2024-06-30T12:00:00+00:00[UTC]", end: "2024-12-31T17:00:00+00:00[UTC]" }]
output 

1. Place two intervals

A
B
Intersection
Union
Difference (A − B)
Symmetric difference (XOR)
Jan 2024JulDec

2. Compare the four operations

Intersection

 

Union

 

Difference (A − B)

 

Symmetric difference (XOR)

 

zoned/interval/intervalXorZoned.ts