@alanszp/business-days-date-fns 4.0.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.eslintrc.js +93 -0
- package/.gitignore +3 -0
- package/.npmignore +3 -0
- package/LICENSE +21 -0
- package/README.MD +51 -0
- package/babel.config.js +7 -0
- package/dist/cache/index.d.ts +1 -0
- package/dist/cache/index.js +17 -0
- package/dist/cache/index.js.map +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +6 -0
- package/dist/index.js.map +1 -0
- package/dist/utils/addBusinessDays.d.ts +1 -0
- package/dist/utils/addBusinessDays.js +42 -0
- package/dist/utils/addBusinessDays.js.map +1 -0
- package/dist/utils/addBusinessDays.test.d.ts +1 -0
- package/dist/utils/addBusinessDays.test.js +94 -0
- package/dist/utils/addBusinessDays.test.js.map +1 -0
- package/dist/utils/differenceInBusinessDay.test.d.ts +1 -0
- package/dist/utils/differenceInBusinessDay.test.js +96 -0
- package/dist/utils/differenceInBusinessDay.test.js.map +1 -0
- package/dist/utils/differenceInBusinessDays.d.ts +1 -0
- package/dist/utils/differenceInBusinessDays.js +34 -0
- package/dist/utils/differenceInBusinessDays.js.map +1 -0
- package/dist/utils/isBusinessDay.d.ts +1 -0
- package/dist/utils/isBusinessDay.js +16 -0
- package/dist/utils/isBusinessDay.js.map +1 -0
- package/dist/utils/isBusinessDay.test.d.ts +1 -0
- package/dist/utils/isBusinessDay.test.js +34 -0
- package/dist/utils/isBusinessDay.test.js.map +1 -0
- package/dist/utils/isNonBusinessDay.d.ts +1 -0
- package/dist/utils/isNonBusinessDay.js +17 -0
- package/dist/utils/isNonBusinessDay.js.map +1 -0
- package/dist/utils/isNonBusinessDay.test.d.ts +1 -0
- package/dist/utils/isNonBusinessDay.test.js +52 -0
- package/dist/utils/isNonBusinessDay.test.js.map +1 -0
- package/dist/utils/requiredArgs.d.ts +1 -0
- package/dist/utils/requiredArgs.js +10 -0
- package/dist/utils/requiredArgs.js.map +1 -0
- package/dist/utils/subBusinessDays.d.ts +1 -0
- package/dist/utils/subBusinessDays.js +14 -0
- package/dist/utils/subBusinessDays.js.map +1 -0
- package/dist/utils/subBusinessDays.test.d.ts +1 -0
- package/dist/utils/subBusinessDays.test.js +69 -0
- package/dist/utils/subBusinessDays.test.js.map +1 -0
- package/dist/utils/withNonBusinessDays.d.ts +11 -0
- package/dist/utils/withNonBusinessDays.js +45 -0
- package/dist/utils/withNonBusinessDays.js.map +1 -0
- package/dist/utils/withNonBusinessDays.test.d.ts +1 -0
- package/dist/utils/withNonBusinessDays.test.js +73 -0
- package/dist/utils/withNonBusinessDays.test.js.map +1 -0
- package/dist/utils/wrapperNonBusinessDays.d.ts +3 -0
- package/dist/utils/wrapperNonBusinessDays.js +70 -0
- package/dist/utils/wrapperNonBusinessDays.js.map +1 -0
- package/header.png +0 -0
- package/jest.config.js +10 -0
- package/package.json +39 -0
- package/src/cache/index.ts +13 -0
- package/src/index.ts +1 -0
- package/src/utils/addBusinessDays.test.ts +131 -0
- package/src/utils/addBusinessDays.ts +46 -0
- package/src/utils/differenceInBusinessDay.test.ts +176 -0
- package/src/utils/differenceInBusinessDays.ts +37 -0
- package/src/utils/isBusinessDay.test.ts +47 -0
- package/src/utils/isBusinessDay.ts +18 -0
- package/src/utils/isNonBusinessDay.test.ts +69 -0
- package/src/utils/isNonBusinessDay.ts +17 -0
- package/src/utils/requiredArgs.ts +5 -0
- package/src/utils/subBusinessDays.test.ts +99 -0
- package/src/utils/subBusinessDays.ts +10 -0
- package/src/utils/withNonBusinessDays.test.ts +82 -0
- package/src/utils/withNonBusinessDays.ts +87 -0
- package/src/utils/wrapperNonBusinessDays.ts +78 -0
- package/tsconfig.json +15 -0
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
import assert from "assert";
|
|
2
|
+
import { addBusinessDays } from "./addBusinessDays";
|
|
3
|
+
|
|
4
|
+
describe("addBusinessDays", () => {
|
|
5
|
+
it("should return 2 next days", () => {
|
|
6
|
+
const result = addBusinessDays(
|
|
7
|
+
[new Date("2022-01-20")],
|
|
8
|
+
new Date("2022-01-19"),
|
|
9
|
+
1
|
|
10
|
+
);
|
|
11
|
+
expect(result).toEqual(new Date("2022-01-21"));
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
it("should return next monday", () => {
|
|
15
|
+
const result = addBusinessDays(
|
|
16
|
+
[new Date("2022-01-20"), new Date("2022-01-21")],
|
|
17
|
+
new Date("2022-01-19"),
|
|
18
|
+
1
|
|
19
|
+
);
|
|
20
|
+
expect(result).toEqual(new Date("2022-01-24"));
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
it("should return next monday when you start on weekend", () => {
|
|
24
|
+
const result = addBusinessDays([], new Date("2022-01-21"), 1);
|
|
25
|
+
expect(result).toEqual(new Date("2022-01-24"));
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
// original tests
|
|
29
|
+
|
|
30
|
+
it("adds the given number of business days", () => {
|
|
31
|
+
const result = addBusinessDays([], new Date(2014, 8 /* Sep */, 1), 10);
|
|
32
|
+
expect(result).toEqual(new Date(2014, 8 /* Sep */, 15));
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
it("handles negative amount", () => {
|
|
36
|
+
const result = addBusinessDays([], new Date(2014, 8 /* Sep */, 15), -10);
|
|
37
|
+
expect(result).toEqual(new Date(2014, 8 /* Sep */, 1));
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
it("returns the Monday when 1 day is added on the Friday", () => {
|
|
41
|
+
expect(
|
|
42
|
+
addBusinessDays([], new Date(2020, 0 /* Jan */, 10), 1) // Friday
|
|
43
|
+
).toEqual(
|
|
44
|
+
new Date(2020, 0 /* Jan */, 13) // Monday
|
|
45
|
+
);
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
it("returns the Monday when 1 day is added on the Saturday", () => {
|
|
49
|
+
expect(
|
|
50
|
+
addBusinessDays([], new Date(2020, 0 /* Jan */, 11), 1) // Saturday
|
|
51
|
+
).toEqual(
|
|
52
|
+
new Date(2020, 0 /* Jan */, 13) // Monday
|
|
53
|
+
);
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
it("returns the Monday when 1 day is added on the Sunday", () => {
|
|
57
|
+
expect(
|
|
58
|
+
addBusinessDays([], new Date(2020, 0 /* Jan */, 12), 1) // Sunday
|
|
59
|
+
).toEqual(
|
|
60
|
+
new Date(2020, 0 /* Jan */, 13) // Monday
|
|
61
|
+
);
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
it("can handle a large number of business days", () => {
|
|
65
|
+
// @ts-ignore
|
|
66
|
+
if (typeof global.timeout === "function") {
|
|
67
|
+
// @ts-ignore
|
|
68
|
+
global.timeout(500 /* 500 ms test timeout */);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
const result = addBusinessDays([], new Date(2014, 0 /* Jan */, 1), 3387885);
|
|
72
|
+
expect(result).toEqual(new Date(15000, 0 /* Jan */, 1));
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
it("accepts a timestamp", () => {
|
|
76
|
+
const result = addBusinessDays(
|
|
77
|
+
[],
|
|
78
|
+
new Date(2014, 8 /* Sep */, 1).getTime(),
|
|
79
|
+
10
|
|
80
|
+
);
|
|
81
|
+
expect(result).toEqual(new Date(2014, 8 /* Sep */, 15));
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
it("converts a fractional number to an integer", () => {
|
|
85
|
+
const result = addBusinessDays([], new Date(2014, 8 /* Sep */, 1), 10.5);
|
|
86
|
+
expect(result).toEqual(new Date(2014, 8 /* Sep */, 15));
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
it("implicitly converts number arguments", () => {
|
|
90
|
+
// @ts-ignore
|
|
91
|
+
const result = addBusinessDays([], new Date(2014, 8 /* Sep */, 1), "10");
|
|
92
|
+
expect(result).toEqual(new Date(2014, 8 /* Sep */, 15));
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
it("does not mutate the original date", () => {
|
|
96
|
+
const date = new Date(2014, 8 /* Sep */, 1);
|
|
97
|
+
addBusinessDays([], date, 11);
|
|
98
|
+
expect(date).toEqual(new Date(2014, 8 /* Sep */, 1));
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
it("returns `Invalid Date` if the given date is invalid", () => {
|
|
102
|
+
const result = addBusinessDays([], new Date(NaN), 10);
|
|
103
|
+
assert(result instanceof Date && Number.isNaN(result.getTime()));
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
it("throws TypeError exception if passed less than 2 arguments", () => {
|
|
107
|
+
try {
|
|
108
|
+
assert.throws(addBusinessDays.bind(null), TypeError);
|
|
109
|
+
|
|
110
|
+
assert.throws(addBusinessDays.bind(null, 1), TypeError);
|
|
111
|
+
} catch {}
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
it("starting from a weekend day should land on a weekday when reducing a divisible by 5", () => {
|
|
115
|
+
const substractResult = addBusinessDays([], new Date(2019, 7, 18), -5);
|
|
116
|
+
expect(substractResult).toEqual(new Date(2019, 7, 12));
|
|
117
|
+
|
|
118
|
+
const subtractResultWeekend = addBusinessDays(
|
|
119
|
+
[],
|
|
120
|
+
new Date(2019, 7, 17),
|
|
121
|
+
-5
|
|
122
|
+
);
|
|
123
|
+
expect(subtractResultWeekend).toEqual(new Date(2019, 7, 12));
|
|
124
|
+
|
|
125
|
+
const addResult = addBusinessDays([], new Date(2019, 7, 18), 5);
|
|
126
|
+
expect(addResult).toEqual(new Date(2019, 7, 23));
|
|
127
|
+
|
|
128
|
+
const addResultWeekend = addBusinessDays([], new Date(2019, 7, 17), 5);
|
|
129
|
+
expect(addResultWeekend).toEqual(new Date(2019, 7, 23));
|
|
130
|
+
});
|
|
131
|
+
});
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import toDate from "date-fns/toDate";
|
|
2
|
+
import isSunday from "date-fns/isSunday";
|
|
3
|
+
import isSaturday from "date-fns/isSaturday";
|
|
4
|
+
import { toInteger } from "lodash";
|
|
5
|
+
import { isNonBusinessDay } from "./isNonBusinessDay";
|
|
6
|
+
import { requiredArgs } from "./requiredArgs";
|
|
7
|
+
|
|
8
|
+
export function addBusinessDays(
|
|
9
|
+
nonBusinessDays: Date[],
|
|
10
|
+
dirtyDate: Date | number,
|
|
11
|
+
dirtyAmount: number
|
|
12
|
+
): Date {
|
|
13
|
+
requiredArgs(2, [dirtyDate, dirtyAmount]);
|
|
14
|
+
|
|
15
|
+
const date = toDate(dirtyDate);
|
|
16
|
+
const startedOnWeekend = isNonBusinessDay(nonBusinessDays, date);
|
|
17
|
+
const amount = toInteger(dirtyAmount);
|
|
18
|
+
|
|
19
|
+
if (Number.isNaN(amount)) return new Date(Number.NaN);
|
|
20
|
+
|
|
21
|
+
const hours = date.getHours();
|
|
22
|
+
const sign = amount < 0 ? -1 : 1;
|
|
23
|
+
const fullWeeks = toInteger(amount / 5);
|
|
24
|
+
|
|
25
|
+
date.setDate(date.getDate() + fullWeeks * 7);
|
|
26
|
+
|
|
27
|
+
let restDays = Math.abs(amount % 5);
|
|
28
|
+
|
|
29
|
+
while (restDays > 0) {
|
|
30
|
+
date.setDate(date.getDate() + sign);
|
|
31
|
+
if (!isNonBusinessDay(nonBusinessDays, date)) restDays -= 1;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
if (
|
|
35
|
+
startedOnWeekend &&
|
|
36
|
+
isNonBusinessDay(nonBusinessDays, date) &&
|
|
37
|
+
amount !== 0
|
|
38
|
+
) {
|
|
39
|
+
if (isSaturday(date)) date.setDate(date.getDate() + (sign < 0 ? 2 : -1));
|
|
40
|
+
if (isSunday(date)) date.setDate(date.getDate() + (sign < 0 ? 1 : -2));
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
date.setHours(hours);
|
|
44
|
+
|
|
45
|
+
return date;
|
|
46
|
+
}
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
import assert from "assert";
|
|
2
|
+
import { differenceInBusinessDays } from "./differenceInBusinessDays";
|
|
3
|
+
|
|
4
|
+
describe("differenceInBusinessDays", () => {
|
|
5
|
+
it("should return 2 difference day taking weekend days", () => {
|
|
6
|
+
const result = differenceInBusinessDays(
|
|
7
|
+
[new Date("2022-01-20"), new Date("2022-01-21")],
|
|
8
|
+
new Date("2022-01-25"),
|
|
9
|
+
new Date("2022-01-19")
|
|
10
|
+
);
|
|
11
|
+
expect(result).toBe(2);
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
it("should return 2 difference", () => {
|
|
15
|
+
const result = differenceInBusinessDays(
|
|
16
|
+
[new Date("2022-01-19")],
|
|
17
|
+
new Date("2022-01-21"),
|
|
18
|
+
new Date("2022-01-18")
|
|
19
|
+
);
|
|
20
|
+
expect(result).toBe(2);
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
// date-fns original tests
|
|
24
|
+
|
|
25
|
+
it("returns the number of business days between the given dates, excluding weekends", () => {
|
|
26
|
+
const result = differenceInBusinessDays(
|
|
27
|
+
[],
|
|
28
|
+
new Date(2014, 6 /* Jul */, 18),
|
|
29
|
+
new Date(2014, 0 /* Jan */, 10)
|
|
30
|
+
);
|
|
31
|
+
expect(result).toEqual(135);
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
it("can handle long ranges", () => {
|
|
35
|
+
// @ts-ignore
|
|
36
|
+
if (typeof global.timeout === "function") {
|
|
37
|
+
// @ts-ignore
|
|
38
|
+
global.timeout(500 /* 500 ms test timeout */);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const result = differenceInBusinessDays(
|
|
42
|
+
[],
|
|
43
|
+
new Date(15000, 0 /* Jan */, 1),
|
|
44
|
+
new Date(2014, 0 /* Jan */, 1)
|
|
45
|
+
);
|
|
46
|
+
expect(result).toEqual(3387885);
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
it("the same except given first date falls on a weekend", () => {
|
|
50
|
+
const result = differenceInBusinessDays(
|
|
51
|
+
[],
|
|
52
|
+
new Date(2019, 6 /* Jul */, 20),
|
|
53
|
+
new Date(2019, 6 /* Jul */, 18)
|
|
54
|
+
);
|
|
55
|
+
expect(result).toEqual(2);
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
it("the same except given second date falls on a weekend", () => {
|
|
59
|
+
const result = differenceInBusinessDays(
|
|
60
|
+
[],
|
|
61
|
+
new Date(2019, 6 /* Jul */, 23),
|
|
62
|
+
new Date(2019, 6 /* Jul */, 20)
|
|
63
|
+
);
|
|
64
|
+
expect(result).toEqual(1);
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
it("the same except both given dates fall on a weekend", () => {
|
|
68
|
+
const result = differenceInBusinessDays(
|
|
69
|
+
[],
|
|
70
|
+
new Date(2019, 6 /* Jul */, 28),
|
|
71
|
+
new Date(2019, 6 /* Jul */, 20)
|
|
72
|
+
);
|
|
73
|
+
expect(result).toEqual(5);
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
it("returns a negative number if the time value of the first date is smaller", () => {
|
|
77
|
+
const result = differenceInBusinessDays(
|
|
78
|
+
[],
|
|
79
|
+
new Date(2014, 0 /* Jan */, 10),
|
|
80
|
+
new Date(2014, 6 /* Jul */, 20)
|
|
81
|
+
);
|
|
82
|
+
expect(result).toEqual(-135);
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
it("accepts timestamps", () => {
|
|
86
|
+
const result = differenceInBusinessDays(
|
|
87
|
+
[],
|
|
88
|
+
new Date(2014, 6, 18).getTime(),
|
|
89
|
+
new Date(2014, 0, 10).getTime()
|
|
90
|
+
);
|
|
91
|
+
expect(result).toEqual(135);
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
it("the difference is less than a day, but the given dates are in different calendar days", () => {
|
|
95
|
+
const result = differenceInBusinessDays(
|
|
96
|
+
[],
|
|
97
|
+
new Date(2014, 8 /* Sep */, 5, 0, 0),
|
|
98
|
+
new Date(2014, 8 /* Sep */, 4, 23, 59)
|
|
99
|
+
);
|
|
100
|
+
expect(result).toEqual(1);
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
it("the same for the swapped dates", () => {
|
|
104
|
+
const result = differenceInBusinessDays(
|
|
105
|
+
[],
|
|
106
|
+
new Date(2014, 8 /* Sep */, 4, 23, 59),
|
|
107
|
+
new Date(2014, 8 /* Sep */, 5, 0, 0)
|
|
108
|
+
);
|
|
109
|
+
expect(result).toEqual(-1);
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
it("the time values of the given dates are the same", () => {
|
|
113
|
+
const result = differenceInBusinessDays(
|
|
114
|
+
[],
|
|
115
|
+
new Date(2014, 8 /* Sep */, 5, 0, 0),
|
|
116
|
+
new Date(2014, 8 /* Sep */, 4, 0, 0)
|
|
117
|
+
);
|
|
118
|
+
expect(result).toEqual(1);
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
it("the given dates are the same", () => {
|
|
122
|
+
const result = differenceInBusinessDays(
|
|
123
|
+
[],
|
|
124
|
+
new Date(2014, 8 /* Sep */, 5, 0, 0),
|
|
125
|
+
new Date(2014, 8 /* Sep */, 5, 0, 0)
|
|
126
|
+
);
|
|
127
|
+
expect(result).toEqual(0);
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
it("does not return -0 when the given dates are the same", () => {
|
|
131
|
+
// eslint-disable-next-line unicorn/consistent-function-scoping
|
|
132
|
+
function isNegativeZero(x: number) {
|
|
133
|
+
return x === 0 && 1 / x < 0;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
const result = differenceInBusinessDays(
|
|
137
|
+
[],
|
|
138
|
+
new Date(2014, 8 /* Sep */, 5, 0, 0),
|
|
139
|
+
new Date(2014, 8 /* Sep */, 5, 0, 0)
|
|
140
|
+
);
|
|
141
|
+
|
|
142
|
+
const resultIsNegative = isNegativeZero(result);
|
|
143
|
+
expect(resultIsNegative).toBeFalsy();
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
it("returns NaN if the first date is `Invalid Date`", () => {
|
|
147
|
+
const result = differenceInBusinessDays(
|
|
148
|
+
[],
|
|
149
|
+
new Date(NaN),
|
|
150
|
+
new Date(2017, 0 /* Jan */, 1)
|
|
151
|
+
);
|
|
152
|
+
expect(Number.isNaN(result)).toBeTruthy();
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
it("returns NaN if the second date is `Invalid Date`", () => {
|
|
156
|
+
const result = differenceInBusinessDays(
|
|
157
|
+
[],
|
|
158
|
+
new Date(2017, 0 /* Jan */, 1),
|
|
159
|
+
new Date(NaN)
|
|
160
|
+
);
|
|
161
|
+
expect(Number.isNaN(result)).toBeTruthy();
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
it("returns NaN if the both dates are `Invalid Date`", () => {
|
|
165
|
+
const result = differenceInBusinessDays([], new Date(NaN), new Date(NaN));
|
|
166
|
+
expect(Number.isNaN(result)).toBeTruthy();
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
it("throws TypeError exception if passed less than 2 arguments", () => {
|
|
170
|
+
try {
|
|
171
|
+
assert.throws(differenceInBusinessDays.bind(null), TypeError);
|
|
172
|
+
|
|
173
|
+
assert.throws(differenceInBusinessDays.bind(null, 1), TypeError);
|
|
174
|
+
} catch {}
|
|
175
|
+
});
|
|
176
|
+
});
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import differenceInCalendarDays from "date-fns/differenceInCalendarDays";
|
|
2
|
+
import addDays from "date-fns/addDays";
|
|
3
|
+
import isSameDay from "date-fns/isSameDay";
|
|
4
|
+
import isValid from "date-fns/isValid";
|
|
5
|
+
import toInteger from "lodash/toInteger";
|
|
6
|
+
import toDate from "date-fns/toDate";
|
|
7
|
+
import { requiredArgs } from "./requiredArgs";
|
|
8
|
+
import { isNonBusinessDay } from "./isNonBusinessDay";
|
|
9
|
+
|
|
10
|
+
export function differenceInBusinessDays(
|
|
11
|
+
nonBusinessDays: Date[],
|
|
12
|
+
dirtyDateLeft: Date | number,
|
|
13
|
+
dirtyDateRight: Date | number
|
|
14
|
+
): number {
|
|
15
|
+
// eslint-disable-next-line prefer-rest-params
|
|
16
|
+
requiredArgs(2, [dirtyDateLeft, dirtyDateRight]);
|
|
17
|
+
|
|
18
|
+
const dateLeft = toDate(dirtyDateLeft);
|
|
19
|
+
let dateRight = toDate(dirtyDateRight);
|
|
20
|
+
|
|
21
|
+
if (!isValid(dateLeft) || !isValid(dateRight)) return Number.NaN;
|
|
22
|
+
|
|
23
|
+
const calendarDifference = differenceInCalendarDays(dateLeft, dateRight);
|
|
24
|
+
const sign = calendarDifference < 0 ? -1 : 1;
|
|
25
|
+
|
|
26
|
+
const weeks = toInteger(calendarDifference / 7);
|
|
27
|
+
|
|
28
|
+
let result = weeks * 5;
|
|
29
|
+
dateRight = addDays(dateRight, weeks * 7);
|
|
30
|
+
|
|
31
|
+
while (!isSameDay(dateLeft, dateRight)) {
|
|
32
|
+
result += isNonBusinessDay(nonBusinessDays, dateRight) ? 0 : sign;
|
|
33
|
+
dateRight = addDays(dateRight, sign);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
return result === 0 ? 0 : result;
|
|
37
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import assert from "assert";
|
|
2
|
+
import { parse } from "date-fns";
|
|
3
|
+
import { isBusinessDay } from "./isBusinessDay";
|
|
4
|
+
|
|
5
|
+
describe("isNonBusinessDay", () => {
|
|
6
|
+
it("should return is non business day", () => {
|
|
7
|
+
const result = isBusinessDay(
|
|
8
|
+
[new Date("2022-01-01"), new Date("2022-01-02")],
|
|
9
|
+
new Date("2022-01-01")
|
|
10
|
+
);
|
|
11
|
+
|
|
12
|
+
expect(result).toBeFalsy();
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
it("should return is a business day", () => {
|
|
16
|
+
const result = isBusinessDay(
|
|
17
|
+
[new Date("2022-01-01"), new Date("2022-01-02")],
|
|
18
|
+
new Date("2022-01-03")
|
|
19
|
+
);
|
|
20
|
+
|
|
21
|
+
expect(result).toBeTruthy();
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
it("should work properly using hh:mm:ss", () => {
|
|
25
|
+
const mondayLate = isBusinessDay(
|
|
26
|
+
[],
|
|
27
|
+
parse("2022-01-24T23:59:59", "yyyy-MM-dd HH:mm:ss", new Date())
|
|
28
|
+
);
|
|
29
|
+
const fridayLate = isBusinessDay(
|
|
30
|
+
[parse("2022-01-28T23:59:59", "yyyy-MM-dd HH:mm:ss", new Date())],
|
|
31
|
+
parse("2022-01-28T23:59:59", "yyyy-MM-dd HH:mm:ss", new Date())
|
|
32
|
+
);
|
|
33
|
+
|
|
34
|
+
expect(mondayLate).toBeTruthy();
|
|
35
|
+
expect(fridayLate).toBeTruthy();
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
it("throws TypeError exception if passed less than 2 arguments", () => {
|
|
39
|
+
try {
|
|
40
|
+
// @ts-ignore
|
|
41
|
+
isBusinessDay([new Date("2022-01-01"), new Date("2022-01-02")]);
|
|
42
|
+
|
|
43
|
+
assert.throws(isBusinessDay.bind(null), TypeError);
|
|
44
|
+
assert.throws(isBusinessDay.bind(null, 1), TypeError);
|
|
45
|
+
} catch {}
|
|
46
|
+
});
|
|
47
|
+
});
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { isWeekend, toDate } from "date-fns";
|
|
2
|
+
import isSameDay from "date-fns/isSameDay";
|
|
3
|
+
import { requiredArgs } from "./requiredArgs";
|
|
4
|
+
|
|
5
|
+
export function isBusinessDay(
|
|
6
|
+
nonBusinessDays: Date[],
|
|
7
|
+
dirtyDate: Date | number
|
|
8
|
+
): boolean {
|
|
9
|
+
requiredArgs(1, [dirtyDate]);
|
|
10
|
+
|
|
11
|
+
const date = toDate(dirtyDate);
|
|
12
|
+
|
|
13
|
+
return (
|
|
14
|
+
!nonBusinessDays.some((nonBusinessDay) =>
|
|
15
|
+
isSameDay(nonBusinessDay, date)
|
|
16
|
+
) && !isWeekend(date)
|
|
17
|
+
);
|
|
18
|
+
}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import assert from "assert";
|
|
2
|
+
import { parse } from "date-fns";
|
|
3
|
+
import { isNonBusinessDay } from "./isNonBusinessDay";
|
|
4
|
+
|
|
5
|
+
describe("isNonBusinessDay", () => {
|
|
6
|
+
it("should return true because is part of NBD", () => {
|
|
7
|
+
const result = isNonBusinessDay(
|
|
8
|
+
[new Date("2022-01-01"), new Date("2022-01-02")],
|
|
9
|
+
new Date("2022-01-01")
|
|
10
|
+
);
|
|
11
|
+
|
|
12
|
+
expect(result).toBe(true);
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
it("should return true because weekend", () => {
|
|
16
|
+
const saturday = isNonBusinessDay([], new Date("2022-01-01"));
|
|
17
|
+
const sunday = isNonBusinessDay([], new Date("2022-01-02"));
|
|
18
|
+
|
|
19
|
+
expect(saturday).toBeTruthy();
|
|
20
|
+
expect(sunday).toBeTruthy();
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
it("should return false because are part of the week", () => {
|
|
24
|
+
const monday = isNonBusinessDay([], new Date("2022-01-03"));
|
|
25
|
+
const tuesday = isNonBusinessDay([], new Date("2022-01-04"));
|
|
26
|
+
const wednesday = isNonBusinessDay([], new Date("2022-01-05"));
|
|
27
|
+
const thursday = isNonBusinessDay([], new Date("2022-01-06"));
|
|
28
|
+
const friday = isNonBusinessDay([], new Date("2022-01-07"));
|
|
29
|
+
|
|
30
|
+
expect(monday).toBeFalsy();
|
|
31
|
+
expect(tuesday).toBeFalsy();
|
|
32
|
+
expect(wednesday).toBeFalsy();
|
|
33
|
+
expect(thursday).toBeFalsy();
|
|
34
|
+
expect(friday).toBeFalsy();
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
it("should work properly using hh:mm:ss", () => {
|
|
38
|
+
const sundayLate = isNonBusinessDay(
|
|
39
|
+
[],
|
|
40
|
+
parse("2022-01-23T23:59:59", "yyyy-MM-dd HH:mm:ss", new Date())
|
|
41
|
+
);
|
|
42
|
+
const dayOffLate = isNonBusinessDay(
|
|
43
|
+
[parse("2022-01-24T23:59:59", "yyyy-MM-dd HH:mm:ss", new Date())],
|
|
44
|
+
parse("2022-01-24T23:59:59", "yyyy-MM-dd HH:mm:ss", new Date())
|
|
45
|
+
);
|
|
46
|
+
|
|
47
|
+
expect(sundayLate).toBeFalsy();
|
|
48
|
+
expect(dayOffLate).toBeFalsy();
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
it("should return false because is not part of is part of NBD", () => {
|
|
52
|
+
const result = isNonBusinessDay(
|
|
53
|
+
[new Date("2022-01-01"), new Date("2022-01-02")],
|
|
54
|
+
new Date("2022-01-03")
|
|
55
|
+
);
|
|
56
|
+
|
|
57
|
+
expect(result).toBe(false);
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
it("throws TypeError exception if passed less than 2 arguments", () => {
|
|
61
|
+
try {
|
|
62
|
+
// @ts-ignore
|
|
63
|
+
isNonBusinessDay([new Date("2022-01-01"), new Date("2022-01-02")]);
|
|
64
|
+
|
|
65
|
+
assert.throws(isNonBusinessDay.bind(null), TypeError);
|
|
66
|
+
assert.throws(isNonBusinessDay.bind(null, 1), TypeError);
|
|
67
|
+
} catch {}
|
|
68
|
+
});
|
|
69
|
+
});
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { isWeekend, toDate } from "date-fns";
|
|
2
|
+
import isSameDay from "date-fns/isSameDay";
|
|
3
|
+
import { requiredArgs } from "./requiredArgs";
|
|
4
|
+
|
|
5
|
+
export function isNonBusinessDay(
|
|
6
|
+
nonBusinessDays: Date[],
|
|
7
|
+
dirtyDate: Date
|
|
8
|
+
): boolean {
|
|
9
|
+
requiredArgs(1, [dirtyDate]);
|
|
10
|
+
|
|
11
|
+
const date = toDate(dirtyDate);
|
|
12
|
+
|
|
13
|
+
return (
|
|
14
|
+
nonBusinessDays.some((nonBusinessDay) => isSameDay(nonBusinessDay, date)) ||
|
|
15
|
+
isWeekend(date)
|
|
16
|
+
);
|
|
17
|
+
}
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import assert from "assert";
|
|
2
|
+
import { subBusinessDays } from "./subBusinessDays";
|
|
3
|
+
|
|
4
|
+
describe("subBusinessDay", () => {
|
|
5
|
+
it("should return 2 next days", () => {
|
|
6
|
+
const result = subBusinessDays(
|
|
7
|
+
[new Date("2022-01-20")],
|
|
8
|
+
new Date("2022-01-21"),
|
|
9
|
+
1
|
|
10
|
+
);
|
|
11
|
+
expect(result).toEqual(new Date("2022-01-19"));
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
it("should return next monday", () => {
|
|
15
|
+
const result = subBusinessDays(
|
|
16
|
+
[new Date("2022-01-20"), new Date("2022-01-21")],
|
|
17
|
+
new Date("2022-01-24"),
|
|
18
|
+
1
|
|
19
|
+
);
|
|
20
|
+
expect(result).toEqual(new Date("2022-01-19"));
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
it("should return next monday when you start on weekend", () => {
|
|
24
|
+
const result = subBusinessDays([], new Date("2022-01-24"), 1);
|
|
25
|
+
expect(result).toEqual(new Date("2022-01-21"));
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
it("substract the given number of business days", () => {
|
|
29
|
+
const result = subBusinessDays([], new Date(2014, 8 /* Sep */, 1), 10);
|
|
30
|
+
expect(result).toEqual(new Date(2014, 7 /* Aug */, 18));
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
it("handles negative amount", () => {
|
|
34
|
+
const result = subBusinessDays([], new Date(2014, 7 /* Sep */, 18), -10);
|
|
35
|
+
expect(result).toEqual(new Date(2014, 8 /* Sep */, 1));
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
it("can handle a large number of business days", () => {
|
|
39
|
+
// @ts-ignore
|
|
40
|
+
if (typeof global.timeout === "function") {
|
|
41
|
+
// @ts-ignore
|
|
42
|
+
global.timeout(500 /* 500 ms test timeout */);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const result = subBusinessDays(
|
|
46
|
+
[],
|
|
47
|
+
new Date(15000, 0 /* Jan */, 1),
|
|
48
|
+
3387885
|
|
49
|
+
);
|
|
50
|
+
expect(result).toEqual(new Date(2014, 0 /* Jan */, 1));
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
it("accepts a timestamp", () => {
|
|
54
|
+
const result = subBusinessDays(
|
|
55
|
+
[],
|
|
56
|
+
new Date(2014, 8 /* Sep */, 1).getTime(),
|
|
57
|
+
10
|
|
58
|
+
);
|
|
59
|
+
expect(result).toEqual(new Date(2014, 7 /* Aug */, 18));
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
it("converts a fractional number to an integer", () => {
|
|
63
|
+
const result = subBusinessDays([], new Date(2014, 8 /* Sep */, 1), 10.5);
|
|
64
|
+
expect(result).toEqual(new Date(2014, 7 /* Aug */, 18));
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
it("implicitly converts number arguments", () => {
|
|
68
|
+
const result = subBusinessDays(
|
|
69
|
+
[],
|
|
70
|
+
new Date(2014, 8 /* Sep */, 1),
|
|
71
|
+
// @ts-ignore
|
|
72
|
+
"10"
|
|
73
|
+
);
|
|
74
|
+
expect(result).toEqual(new Date(2014, 7 /* Aug */, 18));
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
it("does not mutate the original date", () => {
|
|
78
|
+
const date = new Date(2014, 8 /* Sep */, 1);
|
|
79
|
+
subBusinessDays([], date, 11);
|
|
80
|
+
expect(date).toEqual(new Date(2014, 8 /* Sep */, 1));
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
it("returns `Invalid Date` if the given date is invalid", () => {
|
|
84
|
+
const result = subBusinessDays([], new Date(NaN), 10);
|
|
85
|
+
expect(
|
|
86
|
+
result instanceof Date && Number.isNaN(result.getTime())
|
|
87
|
+
).toBeTruthy();
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
it("throws TypeError exception if passed less than 2 arguments", () => {
|
|
91
|
+
try {
|
|
92
|
+
assert.throws(subBusinessDays.bind(null), TypeError);
|
|
93
|
+
assert.throws(
|
|
94
|
+
subBusinessDays.bind(null, new Date(2014, 8 /* Sep */, 1)),
|
|
95
|
+
TypeError
|
|
96
|
+
);
|
|
97
|
+
} catch {}
|
|
98
|
+
});
|
|
99
|
+
});
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { toInteger } from "lodash";
|
|
2
|
+
import { addBusinessDays } from "./addBusinessDays";
|
|
3
|
+
import { requiredArgs } from "./requiredArgs";
|
|
4
|
+
|
|
5
|
+
export function subBusinessDays(nonBusinessDays: Date[], dirtyDate: Date | number, dirtyAmount: number): Date {
|
|
6
|
+
// eslint-disable-next-line prefer-rest-params
|
|
7
|
+
requiredArgs(2, [dirtyDate, dirtyAmount]);
|
|
8
|
+
const amount = toInteger(dirtyAmount);
|
|
9
|
+
return addBusinessDays(nonBusinessDays, dirtyDate, -amount);
|
|
10
|
+
}
|