import { getOrThrow } from "effect/Either"; import assert from "node:assert"; import { describe, it } from "node:test"; import "temporal-polyfill/global"; import { covers, equals, intersection, type Interval, overlaps, parse, stringify, } from "./Interval.js"; namespace Fixtures { export const oneYearInterval: Interval = { start: Temporal.ZonedDateTime.from({ year: 2024, month: 1, day: 1, timeZone: "Europe/Vienna", }), end: Temporal.ZonedDateTime.from({ year: 2025, month: 1, day: 1, timeZone: "Europe/Vienna", }), }; } const add = ( interval: Interval, duration: Temporal.DurationLike, ): Interval => ({ start: interval.start.add(duration), end: interval.end.add(duration), }); const assertIntervalsAreEqual = ( a: Interval | undefined, b: Interval | undefined, msg?: string, ) => assert.equal( a != null && b != null ? equals(a, b) : a == b, true, `expected '${a == null ? a : stringify(a)}' to equal '${b == null ? b : stringify(b)}'${msg == null ? "" : `: ${msg}`}`, ); describe("Interval", () => { describe("stringify", () => { it("should stringify to an ISO8601 compatible string", () => { assert.equal( stringify(Fixtures.oneYearInterval), "2024-01-01T00:00:00+01:00[Europe/Vienna]/2025-01-01T00:00:00+01:00[Europe/Vienna]", ); }); }); describe("parse", () => { it("should be able to parse a stringified interval", () => { assertIntervalsAreEqual( Fixtures.oneYearInterval, getOrThrow(parse(stringify(Fixtures.oneYearInterval))), ); }); }); describe("intersection", () => { it("should return the intersection between two intervals", () => { const rightShifted = { start: Fixtures.oneYearInterval.start.add({ months: 1 }), end: Fixtures.oneYearInterval.end.add({ months: 1 }), }; assertIntervalsAreEqual( intersection(Fixtures.oneYearInterval, rightShifted), { start: rightShifted.start, end: Fixtures.oneYearInterval.end }, "rightshift", ); const leftShifted = { start: Fixtures.oneYearInterval.start.subtract({ months: 1 }), end: Fixtures.oneYearInterval.end.subtract({ months: 1 }), }; assertIntervalsAreEqual( intersection(Fixtures.oneYearInterval, leftShifted), { start: Fixtures.oneYearInterval.start, end: leftShifted.end }, "leftshift", ); const narrowed = { start: Fixtures.oneYearInterval.start.add({ months: 1 }), end: Fixtures.oneYearInterval.end.subtract({ months: 1 }), }; assertIntervalsAreEqual( intersection(Fixtures.oneYearInterval, narrowed), narrowed, "narrowed/large, narrow", ); assertIntervalsAreEqual( intersection(narrowed, Fixtures.oneYearInterval), narrowed, "narrowed/narrow, large", ); }); it("should return undefined if intervals don't overlap", () => { const shifted = add(Fixtures.oneYearInterval, { years: 3 }); const intersected = intersection(Fixtures.oneYearInterval, shifted); assert.equal(intersected, undefined); }); }); describe("overlaps", () => { it("should return false if edges align", () => { assert.equal( overlaps(Fixtures.oneYearInterval, { start: Fixtures.oneYearInterval.start.add({ years: 1 }), end: Fixtures.oneYearInterval.end.add({ years: 1 }), }), false, ); }); it("should return true if edges overlap", () => { assert.equal( overlaps( Fixtures.oneYearInterval, add(Fixtures.oneYearInterval, { months: 1 }), ), true, ); }); it("should return true if a completely covers b", () => { const { start, end } = Fixtures.oneYearInterval; assert.equal( overlaps( { start, end }, { start: start.add({ months: 1 }), end: end.subtract({ months: 1 }) }, ), true, ); }); }); describe("covers", () => { it("should return true if a covers b", () => { assert.equal( covers(Fixtures.oneYearInterval, { start: Fixtures.oneYearInterval.start.add({ months: 1 }), end: Fixtures.oneYearInterval.end.subtract({ months: 1 }), }), true, ); }); it("should return true when edges align", () => { assert.equal( covers(Fixtures.oneYearInterval, Fixtures.oneYearInterval), true, ); }); }); });