Skip to content

Commit

Permalink
chore: refactor and improve docs for range utilities (#2570)
Browse files Browse the repository at this point in the history
  • Loading branch information
gpbl authored Nov 2, 2024
1 parent 0c93f13 commit 9136be4
Show file tree
Hide file tree
Showing 6 changed files with 63 additions and 67 deletions.
4 changes: 2 additions & 2 deletions src/utils/index.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
export * from "./addToRange.js";
export * from "./areRangesOverlapping.js";
export * from "./dateMatchModifiers.js";
export * from "./rangeContainsDayOfWeek.js";
export * from "./rangeIncludesDate.js";
export * from "./rangeContainsModifiers.js";
export * from "./rangeIncludesDate.js";
export * from "./rangeOverlaps.js";
export * from "./typeguards.js";
31 changes: 15 additions & 16 deletions src/utils/rangeContainsDayOfWeek.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { defaultDateLib } from "../classes/DateLib";
import { DayOfWeek } from "../types";

import { rangeContainsDayOfWeek } from "./rangeContainsDayOfWeek";

Expand All @@ -10,10 +9,10 @@ const saturday = new Date(2024, 8, 7); // day of the week 6
const nextWeekSunday = new Date(2024, 8, 8); // day of the week 0

describe("should return false", () => {
const testCases: Array<[{ from: Date; to: Date }, DayOfWeek]> = [
[{ from: monday, to: saturday }, { dayOfWeek: 0 }],
[{ from: monday, to: friday }, { dayOfWeek: [0, 6] }],
[{ from: sunday, to: friday }, { dayOfWeek: 6 }]
const testCases: Array<[{ from: Date; to: Date }, number | number[]]> = [
[{ from: monday, to: saturday }, 0],
[{ from: monday, to: friday }, [0, 6]],
[{ from: sunday, to: friday }, 6]
];

for (const [range, dayOfWeek] of testCases) {
Expand All @@ -26,17 +25,17 @@ describe("should return false", () => {
});

describe("should return true", () => {
const testCases: Array<[{ from: Date; to: Date }, DayOfWeek]> = [
[{ from: sunday, to: saturday }, { dayOfWeek: 0 }],
[{ from: monday, to: friday }, { dayOfWeek: 1 }],
[{ from: monday, to: friday }, { dayOfWeek: 2 }],
[{ from: monday, to: friday }, { dayOfWeek: 3 }],
[{ from: monday, to: friday }, { dayOfWeek: 4 }],
[{ from: monday, to: friday }, { dayOfWeek: 5 }],
[{ from: monday, to: saturday }, { dayOfWeek: 6 }],
[{ from: monday, to: saturday }, { dayOfWeek: [0, 6] }],
[{ from: monday, to: nextWeekSunday }, { dayOfWeek: 0 }],
[{ from: monday, to: nextWeekSunday }, { dayOfWeek: 6 }]
const testCases: Array<[{ from: Date; to: Date }, number | number[]]> = [
[{ from: sunday, to: saturday }, 0],
[{ from: monday, to: friday }, 1],
[{ from: monday, to: friday }, 2],
[{ from: monday, to: friday }, 3],
[{ from: monday, to: friday }, 4],
[{ from: monday, to: friday }, 5],
[{ from: monday, to: saturday }, 6],
[{ from: monday, to: saturday }, [0, 6]],
[{ from: monday, to: nextWeekSunday }, 0],
[{ from: monday, to: nextWeekSunday }, 6]
];

for (const [range, dayOfWeek] of testCases) {
Expand Down
16 changes: 7 additions & 9 deletions src/utils/rangeContainsDayOfWeek.ts
Original file line number Diff line number Diff line change
@@ -1,27 +1,25 @@
import { defaultDateLib, type DateLib } from "../classes/DateLib.js";
import type { DayOfWeek } from "../types/index.js";

/**
* Returns whether a date range matches against a given {@link DayOfWeek}.
* Returns whether a date range contains one or more days of the week.
*
* ```tsx
* const range: DateRange = {
* from: new Date(2024, 8, 1), // day of the week 0
* to: new Date(2024, 8, 6) // day of the week 5
* from: new Date(2024, 8, 1), // Sunday
* to: new Date(2024, 8, 6) // Thursday
* };
* rangeContainsDayOfWeek(date, { dayOfWeek: 0 }); // true
* rangeContainsDayOfWeek(date, 1); // true: contains range contains Monday
* ```
*
* @since 9.2.2
* @group Utilities
*/
export function rangeContainsDayOfWeek(
range: { from: Date; to: Date },
matcher: DayOfWeek,
dayOfWeek: number | number[],
dateLib: DateLib = defaultDateLib
) {
const dayOfWeekArr = !Array.isArray(matcher.dayOfWeek)
? [matcher.dayOfWeek]
: matcher.dayOfWeek;
const dayOfWeekArr = !Array.isArray(dayOfWeek) ? [dayOfWeek] : dayOfWeek;
let date = range.from;
const totalDays = dateLib.differenceInCalendarDays(range.to, range.from);

Expand Down
58 changes: 28 additions & 30 deletions src/utils/rangeContainsModifiers.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { defaultDateLib, type DateLib } from "../classes/DateLib.js";
import type { Matcher } from "../types/index.js";

import { areRangesOverlapping } from "./areRangesOverlapping.js";
import { dateMatchModifiers } from "./dateMatchModifiers.js";
import { rangeContainsDayOfWeek } from "./rangeContainsDayOfWeek.js";
import { rangeIncludesDate } from "./rangeIncludesDate.js";
import { rangeOverlaps } from "./rangeOverlaps.js";
import {
isDateAfterType,
isDateBeforeType,
Expand All @@ -15,8 +15,7 @@ import {
} from "./typeguards.js";

/**
* Returns whether a date range matches against at least one of the given
* {@link Matcher}.
* Returns whether a range contains dates that match the given modifiers.
*
* ```tsx
* const range: DateRange = {
Expand All @@ -28,28 +27,26 @@ import {
* from: new Date(2022, 5, 1),
* to: new Date(2022, 5, 23)
* };
* rangeContainsModifiers(date, [matcher1, matcher2]); // true, since matcher1 is in the date.
* rangeContainsModifiers(range, [matcher1, matcher2]); // true, since matcher1 is in the date.
* ```
*
* @since 9.2.2
* @group Utilities
*/
export function rangeContainsModifiers(
range: { from: Date; to: Date },
matchers: Matcher | Matcher[],
modifiers: Matcher | Matcher[],
dateLib: DateLib = defaultDateLib
): boolean {
const matchersArr = !Array.isArray(matchers) ? [matchers] : matchers;
const matchers = Array.isArray(modifiers) ? modifiers : [modifiers];

// function matchers needs to verified against every day in the date range,
// because of that it's the least performant one and should be deferred to be the last evaluated
const nonFunctionMatchers = matchersArr.filter(
// Defer function matchers evaluation as they are the least performant.
const nonFunctionMatchers = matchers.filter(
(matcher) => typeof matcher !== "function"
);

const nonFunctionMatchersResult = nonFunctionMatchers.some((matcher) => {
if (typeof matcher === "boolean") {
return matcher;
}
if (typeof matcher === "boolean") return matcher;

if (dateLib.isDate(matcher)) {
return rangeIncludesDate(range, matcher, false, dateLib);
Expand All @@ -63,54 +60,55 @@ export function rangeContainsModifiers(

if (isDateRange(matcher)) {
if (matcher.from && matcher.to) {
const dateRangeMatcher = { from: matcher.from, to: matcher.to };
return areRangesOverlapping(range, dateRangeMatcher, dateLib);
return rangeOverlaps(
range,
{ from: matcher.from, to: matcher.to },
dateLib
);
}
return false;
}

if (isDayOfWeekType(matcher)) {
return rangeContainsDayOfWeek(range, matcher, dateLib);
return rangeContainsDayOfWeek(range, matcher.dayOfWeek, dateLib);
}

if (isDateInterval(matcher)) {
const isClosedInterval = dateLib.isAfter(matcher.before, matcher.after);

if (isClosedInterval) {
const dateRangeMatcher = {
from: dateLib.addDays(matcher.after, 1),
to: dateLib.addDays(matcher.before, -1)
};
return areRangesOverlapping(range, dateRangeMatcher, dateLib);
return rangeOverlaps(
range,
{
from: dateLib.addDays(matcher.after, 1),
to: dateLib.addDays(matcher.before, -1)
},
dateLib
);
}

return (
dateMatchModifiers(range.from, matcher, dateLib) ||
dateMatchModifiers(range.to, matcher, dateLib)
);
}
if (isDateAfterType(matcher)) {
return (
dateMatchModifiers(range.from, matcher, dateLib) ||
dateMatchModifiers(range.to, matcher, dateLib)
);
}
if (isDateBeforeType(matcher)) {

if (isDateAfterType(matcher) || isDateBeforeType(matcher)) {
return (
dateMatchModifiers(range.from, matcher, dateLib) ||
dateMatchModifiers(range.to, matcher, dateLib)
);
}

return false;
});

if (nonFunctionMatchersResult) {
return true;
}

const functionMatchers = matchersArr.filter(
const functionMatchers = matchers.filter(
(matcher) => typeof matcher === "function"
);

if (functionMatchers.length) {
let date = range.from;
const totalDays = dateLib.differenceInCalendarDays(range.to, range.from);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { defaultDateLib } from "../classes/DateLib";

import { areRangesOverlapping } from "./areRangesOverlapping";
import { rangeOverlaps } from "./rangeOverlaps";

const sunday = new Date(2024, 8, 1);
const monday = new Date(2024, 8, 2);
Expand All @@ -13,48 +13,48 @@ const leftRange = { from: monday, to: saturday };

test('should return true when matching the "from" date', () => {
const rightRange = { from: sunday, to: monday };
const result = areRangesOverlapping(leftRange, rightRange, defaultDateLib);
const result = rangeOverlaps(leftRange, rightRange, defaultDateLib);
expect(result).toBe(true);
});

test('should return true when matching the "to" date', () => {
const rightRange = { from: saturday, to: nextWeekSunday };
const result = areRangesOverlapping(leftRange, rightRange, defaultDateLib);
const result = rangeOverlaps(leftRange, rightRange, defaultDateLib);
expect(result).toBe(true);
});

test("should return true when left date range contains right date range", () => {
const rightRange = { from: tuesday, to: thursday };
const result = areRangesOverlapping(leftRange, rightRange, defaultDateLib);
const result = rangeOverlaps(leftRange, rightRange, defaultDateLib);
expect(result).toBe(true);
});

test("should return true when right date range contains left date range", () => {
const rightRange = { from: sunday, to: nextWeekSunday };
const result = areRangesOverlapping(leftRange, rightRange, defaultDateLib);
const result = rangeOverlaps(leftRange, rightRange, defaultDateLib);
expect(result).toBe(true);
});

test("should return true when a date range is inverted", () => {
const rightRange = { to: sunday, from: nextWeekSunday };
const result = areRangesOverlapping(leftRange, rightRange, defaultDateLib);
const result = rangeOverlaps(leftRange, rightRange, defaultDateLib);
expect(result).toBe(true);
});

test('should return false on the edge of the "from" date', () => {
const rightRange = { from: new Date(2000, 1, 1), to: sunday };
const result = areRangesOverlapping(leftRange, rightRange, defaultDateLib);
const result = rangeOverlaps(leftRange, rightRange, defaultDateLib);
expect(result).toBe(false);
});

test('should return false on the edge of the "to" date', () => {
const rightRange = { from: nextWeekSunday, to: new Date(2077, 1, 1) };
const result = areRangesOverlapping(leftRange, rightRange, defaultDateLib);
const result = rangeOverlaps(leftRange, rightRange, defaultDateLib);
expect(result).toBe(false);
});

test("should return false when a date range is inverted", () => {
const rightRange = { to: nextWeekSunday, from: new Date(2077, 1, 1) };
const result = areRangesOverlapping(leftRange, rightRange, defaultDateLib);
const result = rangeOverlaps(leftRange, rightRange, defaultDateLib);
expect(result).toBe(false);
});
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,10 @@ import { rangeIncludesDate } from "./rangeIncludesDate.js";
/**
* Determines whether a given range overlaps with another range.
*
* @since 9.2.2
* @group Utilities
*/
export function areRangesOverlapping(
export function rangeOverlaps(
rangeLeft: { from: Date; to: Date },
rangeRight: { from: Date; to: Date },
dateLib = defaultDateLib
Expand Down

0 comments on commit 9136be4

Please sign in to comment.