ts-time-utils 0.0.1 → 1.1.0

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.
Files changed (100) hide show
  1. package/README.md +590 -1
  2. package/dist/age.d.ts +1 -10
  3. package/dist/age.d.ts.map +1 -1
  4. package/dist/calculate.d.ts.map +1 -1
  5. package/dist/calculate.js +24 -10
  6. package/dist/constants.d.ts +2 -21
  7. package/dist/constants.d.ts.map +1 -1
  8. package/dist/constants.js +12 -13
  9. package/dist/countdown.d.ts +217 -0
  10. package/dist/countdown.d.ts.map +1 -0
  11. package/dist/countdown.js +298 -0
  12. package/dist/dateRange.d.ts +266 -0
  13. package/dist/dateRange.d.ts.map +1 -0
  14. package/dist/dateRange.js +433 -0
  15. package/dist/duration.d.ts +171 -0
  16. package/dist/duration.d.ts.map +1 -0
  17. package/dist/duration.js +382 -0
  18. package/dist/esm/age.d.ts +1 -10
  19. package/dist/esm/age.d.ts.map +1 -1
  20. package/dist/esm/calculate.d.ts.map +1 -1
  21. package/dist/esm/calculate.js +24 -10
  22. package/dist/esm/constants.d.ts +2 -21
  23. package/dist/esm/constants.d.ts.map +1 -1
  24. package/dist/esm/constants.js +12 -13
  25. package/dist/esm/countdown.d.ts +217 -0
  26. package/dist/esm/countdown.d.ts.map +1 -0
  27. package/dist/esm/countdown.js +298 -0
  28. package/dist/esm/dateRange.d.ts +266 -0
  29. package/dist/esm/dateRange.d.ts.map +1 -0
  30. package/dist/esm/dateRange.js +433 -0
  31. package/dist/esm/duration.d.ts +171 -0
  32. package/dist/esm/duration.d.ts.map +1 -0
  33. package/dist/esm/duration.js +382 -0
  34. package/dist/esm/format.d.ts.map +1 -1
  35. package/dist/esm/index.d.ts +14 -6
  36. package/dist/esm/index.d.ts.map +1 -1
  37. package/dist/esm/index.js +16 -0
  38. package/dist/esm/interval.d.ts +3 -6
  39. package/dist/esm/interval.d.ts.map +1 -1
  40. package/dist/esm/locale.d.ts +94 -0
  41. package/dist/esm/locale.d.ts.map +1 -0
  42. package/dist/esm/locale.js +1087 -0
  43. package/dist/esm/naturalLanguage.d.ts +107 -0
  44. package/dist/esm/naturalLanguage.d.ts.map +1 -0
  45. package/dist/esm/naturalLanguage.js +344 -0
  46. package/dist/esm/performance.d.ts +2 -9
  47. package/dist/esm/performance.d.ts.map +1 -1
  48. package/dist/esm/performance.js +7 -8
  49. package/dist/esm/rangePresets.d.ts +7 -8
  50. package/dist/esm/rangePresets.d.ts.map +1 -1
  51. package/dist/esm/rangePresets.js +11 -9
  52. package/dist/esm/recurrence.d.ts +149 -0
  53. package/dist/esm/recurrence.d.ts.map +1 -0
  54. package/dist/esm/recurrence.js +404 -0
  55. package/dist/esm/serialize.d.ts +73 -0
  56. package/dist/esm/serialize.d.ts.map +1 -0
  57. package/dist/esm/serialize.js +365 -0
  58. package/dist/esm/timezone.d.ts +2 -6
  59. package/dist/esm/timezone.d.ts.map +1 -1
  60. package/dist/esm/timezone.js +1 -1
  61. package/dist/esm/types.d.ts +250 -0
  62. package/dist/esm/types.d.ts.map +1 -0
  63. package/dist/esm/types.js +25 -0
  64. package/dist/esm/workingHours.d.ts +4 -13
  65. package/dist/esm/workingHours.d.ts.map +1 -1
  66. package/dist/esm/workingHours.js +3 -1
  67. package/dist/format.d.ts.map +1 -1
  68. package/dist/index.d.ts +14 -6
  69. package/dist/index.d.ts.map +1 -1
  70. package/dist/index.js +16 -0
  71. package/dist/interval.d.ts +3 -6
  72. package/dist/interval.d.ts.map +1 -1
  73. package/dist/locale.d.ts +94 -0
  74. package/dist/locale.d.ts.map +1 -0
  75. package/dist/locale.js +1087 -0
  76. package/dist/naturalLanguage.d.ts +107 -0
  77. package/dist/naturalLanguage.d.ts.map +1 -0
  78. package/dist/naturalLanguage.js +344 -0
  79. package/dist/performance.d.ts +2 -9
  80. package/dist/performance.d.ts.map +1 -1
  81. package/dist/performance.js +7 -8
  82. package/dist/rangePresets.d.ts +7 -8
  83. package/dist/rangePresets.d.ts.map +1 -1
  84. package/dist/rangePresets.js +11 -9
  85. package/dist/recurrence.d.ts +149 -0
  86. package/dist/recurrence.d.ts.map +1 -0
  87. package/dist/recurrence.js +404 -0
  88. package/dist/serialize.d.ts +73 -0
  89. package/dist/serialize.d.ts.map +1 -0
  90. package/dist/serialize.js +365 -0
  91. package/dist/timezone.d.ts +2 -6
  92. package/dist/timezone.d.ts.map +1 -1
  93. package/dist/timezone.js +1 -1
  94. package/dist/types.d.ts +250 -0
  95. package/dist/types.d.ts.map +1 -0
  96. package/dist/types.js +25 -0
  97. package/dist/workingHours.d.ts +4 -13
  98. package/dist/workingHours.d.ts.map +1 -1
  99. package/dist/workingHours.js +3 -1
  100. package/package.json +67 -3
@@ -0,0 +1,266 @@
1
+ /**
2
+ * @fileoverview Extended date range operations and utilities
3
+ * Provides advanced operations for working with date ranges beyond basic intervals
4
+ */
5
+ import type { DateRange, DateInput } from './types.js';
6
+ /**
7
+ * Checks if two date ranges overlap
8
+ * @param range1 - First date range
9
+ * @param range2 - Second date range
10
+ * @returns True if the ranges overlap
11
+ *
12
+ * @example
13
+ * ```ts
14
+ * const range1 = { start: new Date('2024-01-01'), end: new Date('2024-01-10') };
15
+ * const range2 = { start: new Date('2024-01-05'), end: new Date('2024-01-15') };
16
+ *
17
+ * dateRangeOverlap(range1, range2); // true
18
+ * ```
19
+ */
20
+ export declare function dateRangeOverlap(range1: DateRange, range2: DateRange): boolean;
21
+ /**
22
+ * Checks if multiple date ranges have any overlaps
23
+ * @param ranges - Array of date ranges
24
+ * @returns True if any two ranges overlap
25
+ *
26
+ * @example
27
+ * ```ts
28
+ * const ranges = [
29
+ * { start: new Date('2024-01-01'), end: new Date('2024-01-10') },
30
+ * { start: new Date('2024-01-05'), end: new Date('2024-01-15') }
31
+ * ];
32
+ *
33
+ * hasOverlappingRanges(ranges); // true
34
+ * ```
35
+ */
36
+ export declare function hasOverlappingRanges(ranges: DateRange[]): boolean;
37
+ /**
38
+ * Merges overlapping or adjacent date ranges
39
+ * @param ranges - Array of date ranges to merge
40
+ * @returns Array of merged, non-overlapping ranges
41
+ *
42
+ * @example
43
+ * ```ts
44
+ * const ranges = [
45
+ * { start: new Date('2024-01-01'), end: new Date('2024-01-10') },
46
+ * { start: new Date('2024-01-05'), end: new Date('2024-01-15') },
47
+ * { start: new Date('2024-01-20'), end: new Date('2024-01-25') }
48
+ * ];
49
+ *
50
+ * mergeDateRanges(ranges);
51
+ * // [
52
+ * // { start: Date('2024-01-01'), end: Date('2024-01-15') },
53
+ * // { start: Date('2024-01-20'), end: Date('2024-01-25') }
54
+ * // ]
55
+ * ```
56
+ */
57
+ export declare function mergeDateRanges(ranges: DateRange[]): DateRange[];
58
+ /**
59
+ * Finds gaps between date ranges within specified bounds
60
+ * @param ranges - Array of date ranges
61
+ * @param bounds - Optional bounds to search within
62
+ * @returns Array of date ranges representing gaps
63
+ *
64
+ * @example
65
+ * ```ts
66
+ * const ranges = [
67
+ * { start: new Date('2024-01-01'), end: new Date('2024-01-05') },
68
+ * { start: new Date('2024-01-10'), end: new Date('2024-01-15') }
69
+ * ];
70
+ *
71
+ * findGaps(ranges, {
72
+ * start: new Date('2024-01-01'),
73
+ * end: new Date('2024-01-20')
74
+ * });
75
+ * // [
76
+ * // { start: Date('2024-01-06'), end: Date('2024-01-09') },
77
+ * // { start: Date('2024-01-16'), end: Date('2024-01-20') }
78
+ * // ]
79
+ * ```
80
+ */
81
+ export declare function findGaps(ranges: DateRange[], bounds?: DateRange): DateRange[];
82
+ /**
83
+ * Splits a date range into smaller chunks
84
+ * @param range - The date range to split
85
+ * @param chunkSize - Size of each chunk
86
+ * @param unit - Unit for chunk size
87
+ * @returns Array of date ranges
88
+ *
89
+ * @example
90
+ * ```ts
91
+ * const range = {
92
+ * start: new Date('2024-01-01'),
93
+ * end: new Date('2024-01-10')
94
+ * };
95
+ *
96
+ * splitRange(range, 3, 'day');
97
+ * // Returns 4 ranges: 3 days, 3 days, 3 days, 1 day
98
+ * ```
99
+ */
100
+ export declare function splitRange(range: DateRange, chunkSize: number, unit: 'millisecond' | 'second' | 'minute' | 'hour' | 'day' | 'week' | 'month' | 'year'): DateRange[];
101
+ /**
102
+ * Checks if a date falls within a date range
103
+ * @param range - The date range
104
+ * @param date - The date to check
105
+ * @param inclusive - Whether to include boundary dates (default: true)
106
+ * @returns True if date is within range
107
+ *
108
+ * @example
109
+ * ```ts
110
+ * const range = {
111
+ * start: new Date('2024-01-01'),
112
+ * end: new Date('2024-01-31')
113
+ * };
114
+ *
115
+ * containsDate(range, new Date('2024-01-15')); // true
116
+ * containsDate(range, new Date('2024-02-01')); // false
117
+ * ```
118
+ */
119
+ export declare function containsDate(range: DateRange, date: DateInput, inclusive?: boolean): boolean;
120
+ /**
121
+ * Gets the intersection of two date ranges
122
+ * @param range1 - First date range
123
+ * @param range2 - Second date range
124
+ * @returns The overlapping range, or null if no overlap
125
+ *
126
+ * @example
127
+ * ```ts
128
+ * const range1 = { start: new Date('2024-01-01'), end: new Date('2024-01-15') };
129
+ * const range2 = { start: new Date('2024-01-10'), end: new Date('2024-01-20') };
130
+ *
131
+ * getIntersection(range1, range2);
132
+ * // { start: Date('2024-01-10'), end: Date('2024-01-15') }
133
+ * ```
134
+ */
135
+ export declare function getIntersection(range1: DateRange, range2: DateRange): DateRange | null;
136
+ /**
137
+ * Gets the union (combined coverage) of two date ranges
138
+ * @param range1 - First date range
139
+ * @param range2 - Second date range
140
+ * @returns The combined range covering both inputs
141
+ *
142
+ * @example
143
+ * ```ts
144
+ * const range1 = { start: new Date('2024-01-01'), end: new Date('2024-01-15') };
145
+ * const range2 = { start: new Date('2024-01-10'), end: new Date('2024-01-20') };
146
+ *
147
+ * getUnion(range1, range2);
148
+ * // { start: Date('2024-01-01'), end: Date('2024-01-20') }
149
+ * ```
150
+ */
151
+ export declare function getUnion(range1: DateRange, range2: DateRange): DateRange;
152
+ /**
153
+ * Subtracts one date range from another
154
+ * @param range - The range to subtract from
155
+ * @param subtract - The range to subtract
156
+ * @returns Array of remaining date ranges (0-2 ranges)
157
+ *
158
+ * @example
159
+ * ```ts
160
+ * const range = { start: new Date('2024-01-01'), end: new Date('2024-01-31') };
161
+ * const subtract = { start: new Date('2024-01-10'), end: new Date('2024-01-20') };
162
+ *
163
+ * subtractRange(range, subtract);
164
+ * // [
165
+ * // { start: Date('2024-01-01'), end: Date('2024-01-09') },
166
+ * // { start: Date('2024-01-21'), end: Date('2024-01-31') }
167
+ * // ]
168
+ * ```
169
+ */
170
+ export declare function subtractRange(range: DateRange, subtract: DateRange): DateRange[];
171
+ /**
172
+ * Calculates the duration of a date range in milliseconds
173
+ * @param range - The date range
174
+ * @returns Duration in milliseconds
175
+ *
176
+ * @example
177
+ * ```ts
178
+ * const range = {
179
+ * start: new Date('2024-01-01'),
180
+ * end: new Date('2024-01-02')
181
+ * };
182
+ *
183
+ * getRangeDuration(range); // 86400000 (1 day in ms)
184
+ * ```
185
+ */
186
+ export declare function getRangeDuration(range: DateRange): number;
187
+ /**
188
+ * Expands a date range by a specified amount
189
+ * @param range - The date range to expand
190
+ * @param amount - Amount to expand by
191
+ * @param unit - Unit for expansion
192
+ * @param options - Expansion options
193
+ * @returns Expanded date range
194
+ *
195
+ * @example
196
+ * ```ts
197
+ * const range = {
198
+ * start: new Date('2024-01-10'),
199
+ * end: new Date('2024-01-20')
200
+ * };
201
+ *
202
+ * expandRange(range, 5, 'day');
203
+ * // { start: Date('2024-01-05'), end: Date('2024-01-25') }
204
+ *
205
+ * expandRange(range, 5, 'day', { direction: 'before' });
206
+ * // { start: Date('2024-01-05'), end: Date('2024-01-20') }
207
+ * ```
208
+ */
209
+ export declare function expandRange(range: DateRange, amount: number, unit: 'millisecond' | 'second' | 'minute' | 'hour' | 'day' | 'week' | 'month' | 'year', options?: {
210
+ direction?: 'both' | 'before' | 'after';
211
+ }): DateRange;
212
+ /**
213
+ * Shrinks a date range by a specified amount
214
+ * @param range - The date range to shrink
215
+ * @param amount - Amount to shrink by
216
+ * @param unit - Unit for shrinking
217
+ * @param options - Shrink options
218
+ * @returns Shrunk date range, or null if result would be invalid
219
+ *
220
+ * @example
221
+ * ```ts
222
+ * const range = {
223
+ * start: new Date('2024-01-01'),
224
+ * end: new Date('2024-01-31')
225
+ * };
226
+ *
227
+ * shrinkRange(range, 5, 'day');
228
+ * // { start: Date('2024-01-06'), end: Date('2024-01-26') }
229
+ * ```
230
+ */
231
+ export declare function shrinkRange(range: DateRange, amount: number, unit: 'millisecond' | 'second' | 'minute' | 'hour' | 'day' | 'week' | 'month' | 'year', options?: {
232
+ direction?: 'both' | 'start' | 'end';
233
+ }): DateRange | null;
234
+ /**
235
+ * Checks if one date range completely contains another
236
+ * @param outer - The potentially containing range
237
+ * @param inner - The potentially contained range
238
+ * @returns True if outer completely contains inner
239
+ *
240
+ * @example
241
+ * ```ts
242
+ * const outer = { start: new Date('2024-01-01'), end: new Date('2024-01-31') };
243
+ * const inner = { start: new Date('2024-01-10'), end: new Date('2024-01-20') };
244
+ *
245
+ * rangeContains(outer, inner); // true
246
+ * ```
247
+ */
248
+ export declare function rangeContains(outer: DateRange, inner: DateRange): boolean;
249
+ /**
250
+ * Sorts an array of date ranges by start date
251
+ * @param ranges - Array of date ranges
252
+ * @param order - Sort order ('asc' or 'desc')
253
+ * @returns Sorted array of date ranges
254
+ *
255
+ * @example
256
+ * ```ts
257
+ * const ranges = [
258
+ * { start: new Date('2024-01-15'), end: new Date('2024-01-20') },
259
+ * { start: new Date('2024-01-01'), end: new Date('2024-01-10') }
260
+ * ];
261
+ *
262
+ * sortRanges(ranges); // Sorted by start date ascending
263
+ * ```
264
+ */
265
+ export declare function sortRanges(ranges: DateRange[], order?: 'asc' | 'desc'): DateRange[];
266
+ //# sourceMappingURL=dateRange.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dateRange.d.ts","sourceRoot":"","sources":["../../src/dateRange.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAGvD;;;;;;;;;;;;;GAaG;AACH,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,SAAS,GAAG,OAAO,CAE9E;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,SAAS,EAAE,GAAG,OAAO,CASjE;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,eAAe,CAAC,MAAM,EAAE,SAAS,EAAE,GAAG,SAAS,EAAE,CA0BhE;AAED;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAgB,QAAQ,CAAC,MAAM,EAAE,SAAS,EAAE,EAAE,MAAM,CAAC,EAAE,SAAS,GAAG,SAAS,EAAE,CA2C7E;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,UAAU,CACxB,KAAK,EAAE,SAAS,EAChB,SAAS,EAAE,MAAM,EACjB,IAAI,EAAE,aAAa,GAAG,QAAQ,GAAG,QAAQ,GAAG,MAAM,GAAG,KAAK,GAAG,MAAM,GAAG,OAAO,GAAG,MAAM,GACrF,SAAS,EAAE,CA2Bb;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,YAAY,CAC1B,KAAK,EAAE,SAAS,EAChB,IAAI,EAAE,SAAS,EACf,SAAS,UAAO,GACf,OAAO,CAQT;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,eAAe,CAAC,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,SAAS,GAAG,SAAS,GAAG,IAAI,CAStF;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,QAAQ,CAAC,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,SAAS,GAAG,SAAS,CAKxE;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,SAAS,EAAE,QAAQ,EAAE,SAAS,GAAG,SAAS,EAAE,CAyBhF;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,SAAS,GAAG,MAAM,CAEzD;AAED;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAgB,WAAW,CACzB,KAAK,EAAE,SAAS,EAChB,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,aAAa,GAAG,QAAQ,GAAG,QAAQ,GAAG,MAAM,GAAG,KAAK,GAAG,MAAM,GAAG,OAAO,GAAG,MAAM,EACtF,OAAO,GAAE;IACP,SAAS,CAAC,EAAE,MAAM,GAAG,QAAQ,GAAG,OAAO,CAAA;CACnC,GACL,SAAS,CAkBX;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,WAAW,CACzB,KAAK,EAAE,SAAS,EAChB,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,aAAa,GAAG,QAAQ,GAAG,QAAQ,GAAG,MAAM,GAAG,KAAK,GAAG,MAAM,GAAG,OAAO,GAAG,MAAM,EACtF,OAAO,GAAE;IACP,SAAS,CAAC,EAAE,MAAM,GAAG,OAAO,GAAG,KAAK,CAAA;CAChC,GACL,SAAS,GAAG,IAAI,CAuBlB;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,SAAS,EAAE,KAAK,EAAE,SAAS,GAAG,OAAO,CAEzE;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,UAAU,CAAC,MAAM,EAAE,SAAS,EAAE,EAAE,KAAK,GAAE,KAAK,GAAG,MAAc,GAAG,SAAS,EAAE,CAO1F"}
@@ -0,0 +1,433 @@
1
+ /**
2
+ * @fileoverview Extended date range operations and utilities
3
+ * Provides advanced operations for working with date ranges beyond basic intervals
4
+ */
5
+ import { addTime } from './calculate.js';
6
+ /**
7
+ * Checks if two date ranges overlap
8
+ * @param range1 - First date range
9
+ * @param range2 - Second date range
10
+ * @returns True if the ranges overlap
11
+ *
12
+ * @example
13
+ * ```ts
14
+ * const range1 = { start: new Date('2024-01-01'), end: new Date('2024-01-10') };
15
+ * const range2 = { start: new Date('2024-01-05'), end: new Date('2024-01-15') };
16
+ *
17
+ * dateRangeOverlap(range1, range2); // true
18
+ * ```
19
+ */
20
+ export function dateRangeOverlap(range1, range2) {
21
+ return range1.start <= range2.end && range2.start <= range1.end;
22
+ }
23
+ /**
24
+ * Checks if multiple date ranges have any overlaps
25
+ * @param ranges - Array of date ranges
26
+ * @returns True if any two ranges overlap
27
+ *
28
+ * @example
29
+ * ```ts
30
+ * const ranges = [
31
+ * { start: new Date('2024-01-01'), end: new Date('2024-01-10') },
32
+ * { start: new Date('2024-01-05'), end: new Date('2024-01-15') }
33
+ * ];
34
+ *
35
+ * hasOverlappingRanges(ranges); // true
36
+ * ```
37
+ */
38
+ export function hasOverlappingRanges(ranges) {
39
+ for (let i = 0; i < ranges.length; i++) {
40
+ for (let j = i + 1; j < ranges.length; j++) {
41
+ if (dateRangeOverlap(ranges[i], ranges[j])) {
42
+ return true;
43
+ }
44
+ }
45
+ }
46
+ return false;
47
+ }
48
+ /**
49
+ * Merges overlapping or adjacent date ranges
50
+ * @param ranges - Array of date ranges to merge
51
+ * @returns Array of merged, non-overlapping ranges
52
+ *
53
+ * @example
54
+ * ```ts
55
+ * const ranges = [
56
+ * { start: new Date('2024-01-01'), end: new Date('2024-01-10') },
57
+ * { start: new Date('2024-01-05'), end: new Date('2024-01-15') },
58
+ * { start: new Date('2024-01-20'), end: new Date('2024-01-25') }
59
+ * ];
60
+ *
61
+ * mergeDateRanges(ranges);
62
+ * // [
63
+ * // { start: Date('2024-01-01'), end: Date('2024-01-15') },
64
+ * // { start: Date('2024-01-20'), end: Date('2024-01-25') }
65
+ * // ]
66
+ * ```
67
+ */
68
+ export function mergeDateRanges(ranges) {
69
+ if (ranges.length === 0)
70
+ return [];
71
+ // Sort ranges by start date
72
+ const sorted = [...ranges].sort((a, b) => a.start.getTime() - b.start.getTime());
73
+ const merged = [{ ...sorted[0] }];
74
+ for (let i = 1; i < sorted.length; i++) {
75
+ const current = sorted[i];
76
+ const lastMerged = merged[merged.length - 1];
77
+ // Check if current range overlaps or is adjacent to last merged range
78
+ if (current.start <= lastMerged.end ||
79
+ current.start.getTime() === lastMerged.end.getTime() + 1) {
80
+ // Merge by extending the end date if needed
81
+ if (current.end > lastMerged.end) {
82
+ lastMerged.end = new Date(current.end);
83
+ }
84
+ }
85
+ else {
86
+ // No overlap, add as new range
87
+ merged.push({ ...current });
88
+ }
89
+ }
90
+ return merged;
91
+ }
92
+ /**
93
+ * Finds gaps between date ranges within specified bounds
94
+ * @param ranges - Array of date ranges
95
+ * @param bounds - Optional bounds to search within
96
+ * @returns Array of date ranges representing gaps
97
+ *
98
+ * @example
99
+ * ```ts
100
+ * const ranges = [
101
+ * { start: new Date('2024-01-01'), end: new Date('2024-01-05') },
102
+ * { start: new Date('2024-01-10'), end: new Date('2024-01-15') }
103
+ * ];
104
+ *
105
+ * findGaps(ranges, {
106
+ * start: new Date('2024-01-01'),
107
+ * end: new Date('2024-01-20')
108
+ * });
109
+ * // [
110
+ * // { start: Date('2024-01-06'), end: Date('2024-01-09') },
111
+ * // { start: Date('2024-01-16'), end: Date('2024-01-20') }
112
+ * // ]
113
+ * ```
114
+ */
115
+ export function findGaps(ranges, bounds) {
116
+ if (ranges.length === 0) {
117
+ return bounds ? [{ ...bounds }] : [];
118
+ }
119
+ // Merge overlapping ranges first
120
+ const merged = mergeDateRanges(ranges);
121
+ // Sort by start date
122
+ const sorted = merged.sort((a, b) => a.start.getTime() - b.start.getTime());
123
+ const gaps = [];
124
+ // Check gap before first range if bounds provided
125
+ if (bounds && sorted[0].start > bounds.start) {
126
+ gaps.push({
127
+ start: new Date(bounds.start),
128
+ end: addTime(sorted[0].start, -1, 'millisecond')
129
+ });
130
+ }
131
+ // Find gaps between ranges
132
+ for (let i = 0; i < sorted.length - 1; i++) {
133
+ const currentEnd = sorted[i].end;
134
+ const nextStart = sorted[i + 1].start;
135
+ if (nextStart > currentEnd) {
136
+ gaps.push({
137
+ start: addTime(currentEnd, 1, 'day'),
138
+ end: addTime(nextStart, -1, 'day')
139
+ });
140
+ }
141
+ }
142
+ // Check gap after last range if bounds provided
143
+ if (bounds && sorted[sorted.length - 1].end < bounds.end) {
144
+ gaps.push({
145
+ start: addTime(sorted[sorted.length - 1].end, 1, 'millisecond'),
146
+ end: new Date(bounds.end)
147
+ });
148
+ }
149
+ return gaps;
150
+ }
151
+ /**
152
+ * Splits a date range into smaller chunks
153
+ * @param range - The date range to split
154
+ * @param chunkSize - Size of each chunk
155
+ * @param unit - Unit for chunk size
156
+ * @returns Array of date ranges
157
+ *
158
+ * @example
159
+ * ```ts
160
+ * const range = {
161
+ * start: new Date('2024-01-01'),
162
+ * end: new Date('2024-01-10')
163
+ * };
164
+ *
165
+ * splitRange(range, 3, 'day');
166
+ * // Returns 4 ranges: 3 days, 3 days, 3 days, 1 day
167
+ * ```
168
+ */
169
+ export function splitRange(range, chunkSize, unit) {
170
+ const chunks = [];
171
+ let current = new Date(range.start);
172
+ const rangeEnd = new Date(range.end);
173
+ // Keep looping while current hasn't passed the end
174
+ while (current.getTime() <= rangeEnd.getTime()) {
175
+ const chunkEnd = addTime(current, chunkSize, unit);
176
+ // Don't go past the range end
177
+ const effectiveEnd = chunkEnd > rangeEnd ? new Date(rangeEnd) : new Date(chunkEnd);
178
+ chunks.push({
179
+ start: new Date(current),
180
+ end: effectiveEnd
181
+ });
182
+ // Move to the next chunk
183
+ current = chunkEnd;
184
+ // If the next chunk would start after the range end, we're done
185
+ if (current.getTime() > rangeEnd.getTime()) {
186
+ break;
187
+ }
188
+ }
189
+ return chunks;
190
+ }
191
+ /**
192
+ * Checks if a date falls within a date range
193
+ * @param range - The date range
194
+ * @param date - The date to check
195
+ * @param inclusive - Whether to include boundary dates (default: true)
196
+ * @returns True if date is within range
197
+ *
198
+ * @example
199
+ * ```ts
200
+ * const range = {
201
+ * start: new Date('2024-01-01'),
202
+ * end: new Date('2024-01-31')
203
+ * };
204
+ *
205
+ * containsDate(range, new Date('2024-01-15')); // true
206
+ * containsDate(range, new Date('2024-02-01')); // false
207
+ * ```
208
+ */
209
+ export function containsDate(range, date, inclusive = true) {
210
+ const checkDate = new Date(date);
211
+ if (inclusive) {
212
+ return checkDate >= range.start && checkDate <= range.end;
213
+ }
214
+ return checkDate > range.start && checkDate < range.end;
215
+ }
216
+ /**
217
+ * Gets the intersection of two date ranges
218
+ * @param range1 - First date range
219
+ * @param range2 - Second date range
220
+ * @returns The overlapping range, or null if no overlap
221
+ *
222
+ * @example
223
+ * ```ts
224
+ * const range1 = { start: new Date('2024-01-01'), end: new Date('2024-01-15') };
225
+ * const range2 = { start: new Date('2024-01-10'), end: new Date('2024-01-20') };
226
+ *
227
+ * getIntersection(range1, range2);
228
+ * // { start: Date('2024-01-10'), end: Date('2024-01-15') }
229
+ * ```
230
+ */
231
+ export function getIntersection(range1, range2) {
232
+ if (!dateRangeOverlap(range1, range2)) {
233
+ return null;
234
+ }
235
+ return {
236
+ start: new Date(Math.max(range1.start.getTime(), range2.start.getTime())),
237
+ end: new Date(Math.min(range1.end.getTime(), range2.end.getTime()))
238
+ };
239
+ }
240
+ /**
241
+ * Gets the union (combined coverage) of two date ranges
242
+ * @param range1 - First date range
243
+ * @param range2 - Second date range
244
+ * @returns The combined range covering both inputs
245
+ *
246
+ * @example
247
+ * ```ts
248
+ * const range1 = { start: new Date('2024-01-01'), end: new Date('2024-01-15') };
249
+ * const range2 = { start: new Date('2024-01-10'), end: new Date('2024-01-20') };
250
+ *
251
+ * getUnion(range1, range2);
252
+ * // { start: Date('2024-01-01'), end: Date('2024-01-20') }
253
+ * ```
254
+ */
255
+ export function getUnion(range1, range2) {
256
+ return {
257
+ start: new Date(Math.min(range1.start.getTime(), range2.start.getTime())),
258
+ end: new Date(Math.max(range1.end.getTime(), range2.end.getTime()))
259
+ };
260
+ }
261
+ /**
262
+ * Subtracts one date range from another
263
+ * @param range - The range to subtract from
264
+ * @param subtract - The range to subtract
265
+ * @returns Array of remaining date ranges (0-2 ranges)
266
+ *
267
+ * @example
268
+ * ```ts
269
+ * const range = { start: new Date('2024-01-01'), end: new Date('2024-01-31') };
270
+ * const subtract = { start: new Date('2024-01-10'), end: new Date('2024-01-20') };
271
+ *
272
+ * subtractRange(range, subtract);
273
+ * // [
274
+ * // { start: Date('2024-01-01'), end: Date('2024-01-09') },
275
+ * // { start: Date('2024-01-21'), end: Date('2024-01-31') }
276
+ * // ]
277
+ * ```
278
+ */
279
+ export function subtractRange(range, subtract) {
280
+ // No overlap, return original range
281
+ if (!dateRangeOverlap(range, subtract)) {
282
+ return [{ ...range }];
283
+ }
284
+ const result = [];
285
+ // Check if there's a range before the subtraction
286
+ if (range.start < subtract.start) {
287
+ result.push({
288
+ start: new Date(range.start),
289
+ end: addTime(subtract.start, -1, 'day')
290
+ });
291
+ }
292
+ // Check if there's a range after the subtraction
293
+ if (range.end > subtract.end) {
294
+ result.push({
295
+ start: addTime(subtract.end, 1, 'day'),
296
+ end: new Date(range.end)
297
+ });
298
+ }
299
+ return result;
300
+ }
301
+ /**
302
+ * Calculates the duration of a date range in milliseconds
303
+ * @param range - The date range
304
+ * @returns Duration in milliseconds
305
+ *
306
+ * @example
307
+ * ```ts
308
+ * const range = {
309
+ * start: new Date('2024-01-01'),
310
+ * end: new Date('2024-01-02')
311
+ * };
312
+ *
313
+ * getRangeDuration(range); // 86400000 (1 day in ms)
314
+ * ```
315
+ */
316
+ export function getRangeDuration(range) {
317
+ return range.end.getTime() - range.start.getTime();
318
+ }
319
+ /**
320
+ * Expands a date range by a specified amount
321
+ * @param range - The date range to expand
322
+ * @param amount - Amount to expand by
323
+ * @param unit - Unit for expansion
324
+ * @param options - Expansion options
325
+ * @returns Expanded date range
326
+ *
327
+ * @example
328
+ * ```ts
329
+ * const range = {
330
+ * start: new Date('2024-01-10'),
331
+ * end: new Date('2024-01-20')
332
+ * };
333
+ *
334
+ * expandRange(range, 5, 'day');
335
+ * // { start: Date('2024-01-05'), end: Date('2024-01-25') }
336
+ *
337
+ * expandRange(range, 5, 'day', { direction: 'before' });
338
+ * // { start: Date('2024-01-05'), end: Date('2024-01-20') }
339
+ * ```
340
+ */
341
+ export function expandRange(range, amount, unit, options = {}) {
342
+ const { direction = 'both' } = options;
343
+ let newStart = new Date(range.start);
344
+ let newEnd = new Date(range.end);
345
+ if (direction === 'both' || direction === 'before') {
346
+ newStart = addTime(newStart, -amount, unit);
347
+ }
348
+ if (direction === 'both' || direction === 'after') {
349
+ newEnd = addTime(newEnd, amount, unit);
350
+ }
351
+ return {
352
+ start: newStart,
353
+ end: newEnd
354
+ };
355
+ }
356
+ /**
357
+ * Shrinks a date range by a specified amount
358
+ * @param range - The date range to shrink
359
+ * @param amount - Amount to shrink by
360
+ * @param unit - Unit for shrinking
361
+ * @param options - Shrink options
362
+ * @returns Shrunk date range, or null if result would be invalid
363
+ *
364
+ * @example
365
+ * ```ts
366
+ * const range = {
367
+ * start: new Date('2024-01-01'),
368
+ * end: new Date('2024-01-31')
369
+ * };
370
+ *
371
+ * shrinkRange(range, 5, 'day');
372
+ * // { start: Date('2024-01-06'), end: Date('2024-01-26') }
373
+ * ```
374
+ */
375
+ export function shrinkRange(range, amount, unit, options = {}) {
376
+ const { direction = 'both' } = options;
377
+ let newStart = new Date(range.start);
378
+ let newEnd = new Date(range.end);
379
+ if (direction === 'both' || direction === 'start') {
380
+ newStart = addTime(newStart, amount, unit);
381
+ }
382
+ if (direction === 'both' || direction === 'end') {
383
+ newEnd = addTime(newEnd, -amount, unit);
384
+ }
385
+ // Check if result is valid
386
+ if (newStart >= newEnd) {
387
+ return null;
388
+ }
389
+ return {
390
+ start: newStart,
391
+ end: newEnd
392
+ };
393
+ }
394
+ /**
395
+ * Checks if one date range completely contains another
396
+ * @param outer - The potentially containing range
397
+ * @param inner - The potentially contained range
398
+ * @returns True if outer completely contains inner
399
+ *
400
+ * @example
401
+ * ```ts
402
+ * const outer = { start: new Date('2024-01-01'), end: new Date('2024-01-31') };
403
+ * const inner = { start: new Date('2024-01-10'), end: new Date('2024-01-20') };
404
+ *
405
+ * rangeContains(outer, inner); // true
406
+ * ```
407
+ */
408
+ export function rangeContains(outer, inner) {
409
+ return outer.start <= inner.start && outer.end >= inner.end;
410
+ }
411
+ /**
412
+ * Sorts an array of date ranges by start date
413
+ * @param ranges - Array of date ranges
414
+ * @param order - Sort order ('asc' or 'desc')
415
+ * @returns Sorted array of date ranges
416
+ *
417
+ * @example
418
+ * ```ts
419
+ * const ranges = [
420
+ * { start: new Date('2024-01-15'), end: new Date('2024-01-20') },
421
+ * { start: new Date('2024-01-01'), end: new Date('2024-01-10') }
422
+ * ];
423
+ *
424
+ * sortRanges(ranges); // Sorted by start date ascending
425
+ * ```
426
+ */
427
+ export function sortRanges(ranges, order = 'asc') {
428
+ const sorted = [...ranges].sort((a, b) => {
429
+ const diff = a.start.getTime() - b.start.getTime();
430
+ return order === 'asc' ? diff : -diff;
431
+ });
432
+ return sorted;
433
+ }