chronos-ts 1.0.2 → 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.
- package/README.md +42 -30
- package/dist/index.d.ts +1 -0
- package/dist/index.js +15 -0
- package/dist/period.d.ts +11 -0
- package/dist/period.js +55 -16
- package/dist/utils.d.ts +2 -0
- package/dist/utils.js +53 -16
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -2,24 +2,36 @@
|
|
|
2
2
|
Chronos-ts from the name is inspired by the Greek god of time, Chronos. It is a comprehensive TypeScript package for handling time periods, intervals, and date-related operations.
|
|
3
3
|
|
|
4
4
|
## Table of Contents
|
|
5
|
-
- [
|
|
6
|
-
- [
|
|
7
|
-
- [
|
|
8
|
-
- [
|
|
5
|
+
- [Chronos-ts ⏰](#chronos-ts-)
|
|
6
|
+
- [Table of Contents](#table-of-contents)
|
|
7
|
+
- [Installation](#installation)
|
|
8
|
+
- [Overview](#overview)
|
|
9
|
+
- [Usage](#usage)
|
|
10
|
+
- [Creating and Manipulating Periods](#creating-and-manipulating-periods)
|
|
11
|
+
- [Working with Intervals](#working-with-intervals)
|
|
12
|
+
- [Using Utility Functions](#using-utility-functions)
|
|
13
|
+
- [API Reference](#api-reference)
|
|
9
14
|
- [Classes](#classes)
|
|
10
|
-
|
|
11
|
-
- [
|
|
12
|
-
|
|
13
|
-
|
|
15
|
+
- [Period](#period)
|
|
16
|
+
- [Constructor](#constructor)
|
|
17
|
+
- [Methods](#methods)
|
|
18
|
+
- [Fluent API Methods](#fluent-api-methods)
|
|
19
|
+
- [Interval](#interval)
|
|
20
|
+
- [Methods](#methods-1)
|
|
21
|
+
- [Enums](#enums)
|
|
22
|
+
- [Precision](#precision)
|
|
14
23
|
- [Utility Function](#utility-function)
|
|
15
24
|
- [Real-world Scenarios](#real-world-scenarios)
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
25
|
+
- [**1. Event Planning and Management**](#1-event-planning-and-management)
|
|
26
|
+
- [**2. Financial Reporting Periods**](#2-financial--reporting-periods)
|
|
27
|
+
- [**3. Employee Leave Management**](#3-employee-leave-management)
|
|
28
|
+
- [**4. Project Timeline Management**](#4--project-timeline-management)
|
|
29
|
+
- [**5. Subscription Billing Cycle Management**](#5-subscription-billing-cycle-management)
|
|
30
|
+
- [**6. Employee Shift Management**](#6-employee-shift-management)
|
|
31
|
+
- [**7. Travel Itinerary Planning**](#7-travel-itinerary-planning)
|
|
32
|
+
- [Contributing 🤝](#contributing-)
|
|
33
|
+
- [License 📝](#license-)
|
|
34
|
+
- [Acknowledgements 🙏](#acknowledgements-)
|
|
23
35
|
|
|
24
36
|
## Installation
|
|
25
37
|
```bash
|
|
@@ -48,7 +60,7 @@ Whether you're building scheduling systems, financial applications, or any proje
|
|
|
48
60
|
|
|
49
61
|
## Usage
|
|
50
62
|
### Creating and Manipulating Periods
|
|
51
|
-
|
|
63
|
+
```typescript
|
|
52
64
|
import { Period, Precision, Interval } from 'chronos-ts';
|
|
53
65
|
|
|
54
66
|
// Create a period for the year 2023
|
|
@@ -70,8 +82,8 @@ console.log(overlap?.getStartDate(), overlap?.getEndDate()); // 2023-04-01, 2023
|
|
|
70
82
|
// Subtract a period
|
|
71
83
|
const remainingPeriods = year2023.subtract(q2_2023);
|
|
72
84
|
console.log(remainingPeriods.length); // 2
|
|
73
|
-
console.log(remainingPeriods[0].getStartDate(), remainingPeriods[0].getEndDate()); // 2023-01-01, 2023-
|
|
74
|
-
console.log(remainingPeriods[1].getStartDate(), remainingPeriods[1].getEndDate()); // 2023-
|
|
85
|
+
console.log(remainingPeriods[0].getStartDate(), remainingPeriods[0].getEndDate()); // 2023-01-01, 2023-04-01
|
|
86
|
+
console.log(remainingPeriods[1].getStartDate(), remainingPeriods[1].getEndDate()); // 2023-06-30, 2023-12-31
|
|
75
87
|
|
|
76
88
|
// Create a period with an interval
|
|
77
89
|
const weeklyPeriod = new Period('2023-01-01', '2023-12-31', Precision.WEEK, Interval.weeks(1));
|
|
@@ -80,7 +92,7 @@ const weeklyPeriod = new Period('2023-01-01', '2023-12-31', Precision.WEEK, Inte
|
|
|
80
92
|
const weeklyDates = weeklyPeriod.getDatesInInterval();
|
|
81
93
|
console.log(weeklyDates?.length); // 53 (number of weeks in 2023)
|
|
82
94
|
|
|
83
|
-
// Renew a period
|
|
95
|
+
// Renew a period - FIXED: Use getter methods
|
|
84
96
|
const nextYear = year2023.renew();
|
|
85
97
|
console.log(nextYear.getStartDate(), nextYear.getEndDate()); // 2024-01-01, 2024-12-31
|
|
86
98
|
|
|
@@ -90,7 +102,7 @@ const customPeriod = new Period('2023-01-01', '2023-12-31')
|
|
|
90
102
|
.setInterval(Interval.months(3));
|
|
91
103
|
|
|
92
104
|
console.log(customPeriod.getDatesInInterval()?.length); // 5 (Jan, Apr, Jul, Oct, Jan)
|
|
93
|
-
|
|
105
|
+
```
|
|
94
106
|
|
|
95
107
|
### Working with Intervals
|
|
96
108
|
``` typescript
|
|
@@ -218,7 +230,7 @@ The package includes various utility functions for working with dates:
|
|
|
218
230
|
- `range(start: number, end: number, step: number = 1): number[]` - Returns an array of numbers within the specified range.
|
|
219
231
|
|
|
220
232
|
### Real-world Scenarios
|
|
221
|
-
**1. Event Planning and Management**
|
|
233
|
+
#### **1. Event Planning and Management**
|
|
222
234
|
The `Period` class can be used to represent events, while the `Interval` class can help manage recurring events.
|
|
223
235
|
``` typescript
|
|
224
236
|
import { Period, Interval, Precision } from 'chronos-ts';
|
|
@@ -240,7 +252,7 @@ const conflictingMeetings = meetingDates?.filter(date => conference.contains(dat
|
|
|
240
252
|
console.log(`There are ${conflictingMeetings.length} conflicting meetings during the conference.`);
|
|
241
253
|
```
|
|
242
254
|
|
|
243
|
-
**2. Financial Reporting Periods**
|
|
255
|
+
#### **2. Financial Reporting Periods**
|
|
244
256
|
Use the `Period` class to represent financial quarters and calculate year-to-date periods.
|
|
245
257
|
``` typescript
|
|
246
258
|
import { Period, Precision } from 'chronos-ts';
|
|
@@ -250,9 +262,9 @@ const q2_2023 = new Period('2023-04-01', '2023-06-30', Precision.DAY);
|
|
|
250
262
|
const q3_2023 = new Period('2023-07-01', '2023-09-30', Precision.DAY);
|
|
251
263
|
const q4_2023 = new Period('2023-10-01', '2023-12-31', Precision.DAY);
|
|
252
264
|
|
|
253
|
-
// Calculate Year-to-Date period
|
|
265
|
+
// Calculate Year-to-Date period - FIXED: Use getter method
|
|
254
266
|
const ytd = (currentQuarter: Period): Period => {
|
|
255
|
-
return new Period('2023-01-01', currentQuarter.
|
|
267
|
+
return new Period('2023-01-01', currentQuarter.getEndDate(), Precision.DAY);
|
|
256
268
|
};
|
|
257
269
|
|
|
258
270
|
const q3YTD = ytd(q3_2023);
|
|
@@ -269,7 +281,7 @@ const q3Revenue = 1200000;
|
|
|
269
281
|
console.log(`Q3 QoQ Growth: ${calculateQoQGrowth(q3Revenue, q2Revenue)}`);
|
|
270
282
|
```
|
|
271
283
|
|
|
272
|
-
**3. Employee Leave Management**
|
|
284
|
+
#### **3. Employee Leave Management**
|
|
273
285
|
Use the `Period` class to manage employee leave requests and calculate leave balances.
|
|
274
286
|
``` typescript
|
|
275
287
|
import { Period, Precision } from 'chronos-ts';
|
|
@@ -298,7 +310,7 @@ console.log(`Next year's leave balance: ${nextYearLeaveBalance ? nextYearLeaveBa
|
|
|
298
310
|
|
|
299
311
|
```
|
|
300
312
|
|
|
301
|
-
**4. Project Timeline Management**
|
|
313
|
+
#### **4. Project Timeline Management**
|
|
302
314
|
Use the Period class to manage project timelines and track overlapping tasks.
|
|
303
315
|
``` typescript
|
|
304
316
|
import { Period, Precision } from 'chronos-ts';
|
|
@@ -339,7 +351,7 @@ const currentProgress = calculateProgress(new Date('2023-06-15'));
|
|
|
339
351
|
console.log(`Project progress: ${currentProgress.toFixed(2)}%`);
|
|
340
352
|
```
|
|
341
353
|
|
|
342
|
-
**5. Subscription Billing Cycle Management**
|
|
354
|
+
#### **5. Subscription Billing Cycle Management**
|
|
343
355
|
Use the Period class to manage subscription periods and calculate renewal dates.
|
|
344
356
|
``` typescript
|
|
345
357
|
import { Period, Precision, addToDate } from 'chronos-ts';
|
|
@@ -384,7 +396,7 @@ const renewedPeriod = monthlySubscription.getCurrentPeriod();
|
|
|
384
396
|
console.log(`Monthly subscription renewed. New period: ${renewedPeriod.getStartDate().toDateString()} - ${renewedPeriod.getEndDate().toDateString()}`);
|
|
385
397
|
```
|
|
386
398
|
|
|
387
|
-
**6. Employee Shift Management**
|
|
399
|
+
#### **6. Employee Shift Management**
|
|
388
400
|
|
|
389
401
|
Use the `Period` and `Interval` classes to manage employee shifts and calculate overtime.
|
|
390
402
|
|
|
@@ -446,7 +458,7 @@ const hasConflict = (shifts: Shift[]): boolean => {
|
|
|
446
458
|
console.log(`Shifts have conflicts: ${hasConflict(employeeShifts)}`);
|
|
447
459
|
```
|
|
448
460
|
|
|
449
|
-
**7. Travel Itinerary Planning**
|
|
461
|
+
#### **7. Travel Itinerary Planning**
|
|
450
462
|
Use the `Period` class to manage travel itineraries and check for scheduling conflicts.
|
|
451
463
|
``` typescript
|
|
452
464
|
import { Period, Precision, addToDate } from 'chronos-ts';
|
|
@@ -510,4 +522,4 @@ Contributions, issues and feature requests are welcome. After cloning & setting
|
|
|
510
522
|
This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
|
|
511
523
|
|
|
512
524
|
## Acknowledgements 🙏
|
|
513
|
-
- [spatie/period](
|
|
525
|
+
- [spatie/period](https://github.com/spatie/period)
|
package/dist/index.d.ts
CHANGED
package/dist/index.js
CHANGED
|
@@ -1,4 +1,18 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
+
};
|
|
2
16
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
17
|
exports.Interval = exports.Precision = exports.Period = void 0;
|
|
4
18
|
var period_1 = require("./period");
|
|
@@ -7,3 +21,4 @@ var precision_1 = require("./precision");
|
|
|
7
21
|
Object.defineProperty(exports, "Precision", { enumerable: true, get: function () { return precision_1.Precision; } });
|
|
8
22
|
var interval_1 = require("./interval");
|
|
9
23
|
Object.defineProperty(exports, "Interval", { enumerable: true, get: function () { return interval_1.Interval; } });
|
|
24
|
+
__exportStar(require("./utils"), exports);
|
package/dist/period.d.ts
CHANGED
|
@@ -12,8 +12,17 @@ export declare class Period {
|
|
|
12
12
|
* @param end - The end date of the period, either as a string or a Date object.
|
|
13
13
|
* @param precision - The precision level of the period, defaulting to Precision.DAY.
|
|
14
14
|
* @param interval - An optional interval associated with the period, defaulting to null.
|
|
15
|
+
* @throws {Error} If start date is after end date or if dates are invalid.
|
|
15
16
|
*/
|
|
16
17
|
constructor(start: string | Date, end: string | Date, precision?: Precision, interval?: Interval | null);
|
|
18
|
+
/**
|
|
19
|
+
* Validates and converts a date input to a Date object.
|
|
20
|
+
*
|
|
21
|
+
* @param date - The date to validate, either as a string or Date object.
|
|
22
|
+
* @returns A valid Date object.
|
|
23
|
+
* @throws {Error} If the date is invalid.
|
|
24
|
+
*/
|
|
25
|
+
private validateDate;
|
|
17
26
|
/**
|
|
18
27
|
* Retrieves the start date of the period.
|
|
19
28
|
*
|
|
@@ -159,6 +168,7 @@ export declare class Period {
|
|
|
159
168
|
*
|
|
160
169
|
* @param start - The start date, which can be a string or a Date object.
|
|
161
170
|
* @returns The current instance for method chaining.
|
|
171
|
+
* @throws {Error} If the new start date is after the current end date.
|
|
162
172
|
*/
|
|
163
173
|
setStart(start: string | Date): this;
|
|
164
174
|
/**
|
|
@@ -166,6 +176,7 @@ export declare class Period {
|
|
|
166
176
|
*
|
|
167
177
|
* @param end - The end date as a string or Date object.
|
|
168
178
|
* @returns The current instance for method chaining.
|
|
179
|
+
* @throws {Error} If the new end date is before the current start date.
|
|
169
180
|
*/
|
|
170
181
|
setEnd(end: string | Date): this;
|
|
171
182
|
/**
|
package/dist/period.js
CHANGED
|
@@ -11,13 +11,31 @@ class Period {
|
|
|
11
11
|
* @param end - The end date of the period, either as a string or a Date object.
|
|
12
12
|
* @param precision - The precision level of the period, defaulting to Precision.DAY.
|
|
13
13
|
* @param interval - An optional interval associated with the period, defaulting to null.
|
|
14
|
+
* @throws {Error} If start date is after end date or if dates are invalid.
|
|
14
15
|
*/
|
|
15
16
|
constructor(start, end, precision = precision_1.Precision.DAY, interval = null) {
|
|
16
|
-
this.startDate =
|
|
17
|
-
this.endDate =
|
|
17
|
+
this.startDate = this.validateDate(start);
|
|
18
|
+
this.endDate = this.validateDate(end);
|
|
19
|
+
if (this.startDate > this.endDate) {
|
|
20
|
+
throw new Error('Start date must be before or equal to end date');
|
|
21
|
+
}
|
|
18
22
|
this.precision = precision;
|
|
19
23
|
this.interval = interval;
|
|
20
24
|
}
|
|
25
|
+
/**
|
|
26
|
+
* Validates and converts a date input to a Date object.
|
|
27
|
+
*
|
|
28
|
+
* @param date - The date to validate, either as a string or Date object.
|
|
29
|
+
* @returns A valid Date object.
|
|
30
|
+
* @throws {Error} If the date is invalid.
|
|
31
|
+
*/
|
|
32
|
+
validateDate(date) {
|
|
33
|
+
const result = new Date(date);
|
|
34
|
+
if (isNaN(result.getTime())) {
|
|
35
|
+
throw new Error(`Invalid date: ${date}`);
|
|
36
|
+
}
|
|
37
|
+
return result;
|
|
38
|
+
}
|
|
21
39
|
/**
|
|
22
40
|
* Retrieves the start date of the period.
|
|
23
41
|
*
|
|
@@ -41,7 +59,7 @@ class Period {
|
|
|
41
59
|
* @returns `true` if the date is within the period, `false` otherwise.
|
|
42
60
|
*/
|
|
43
61
|
contains(date) {
|
|
44
|
-
const checkDate =
|
|
62
|
+
const checkDate = this.validateDate(date);
|
|
45
63
|
return checkDate >= this.startDate && checkDate <= this.endDate;
|
|
46
64
|
}
|
|
47
65
|
/**
|
|
@@ -138,7 +156,7 @@ class Period {
|
|
|
138
156
|
if (this.endDate.getDate() < this.startDate.getDate()) {
|
|
139
157
|
months--;
|
|
140
158
|
}
|
|
141
|
-
return months;
|
|
159
|
+
return Math.max(0, months);
|
|
142
160
|
}
|
|
143
161
|
/**
|
|
144
162
|
* Calculates the number of whole years in the interval.
|
|
@@ -184,13 +202,13 @@ class Period {
|
|
|
184
202
|
return [this];
|
|
185
203
|
}
|
|
186
204
|
const periods = [];
|
|
205
|
+
// Add period before the overlap (if any)
|
|
187
206
|
if (this.startDate < other.startDate) {
|
|
188
|
-
|
|
189
|
-
periods.push(new Period(this.startDate, endDate, this.precision));
|
|
207
|
+
periods.push(new Period(this.startDate, other.startDate, this.precision));
|
|
190
208
|
}
|
|
209
|
+
// Add period after the overlap (if any)
|
|
191
210
|
if (this.endDate > other.endDate) {
|
|
192
|
-
|
|
193
|
-
periods.push(new Period(startDate, this.endDate, this.precision));
|
|
211
|
+
periods.push(new Period(other.endDate, this.endDate, this.precision));
|
|
194
212
|
}
|
|
195
213
|
return periods;
|
|
196
214
|
}
|
|
@@ -206,10 +224,20 @@ class Period {
|
|
|
206
224
|
if (this.overlapsWith(other) || this.isAdjacentTo(other)) {
|
|
207
225
|
return null;
|
|
208
226
|
}
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
227
|
+
let start, end;
|
|
228
|
+
if (this.endDate < other.startDate) {
|
|
229
|
+
start = this.endDate;
|
|
230
|
+
end = other.startDate;
|
|
231
|
+
}
|
|
232
|
+
else {
|
|
233
|
+
start = other.endDate;
|
|
234
|
+
end = this.startDate;
|
|
235
|
+
}
|
|
236
|
+
// Ensure start is before end for valid Period creation
|
|
237
|
+
if (start >= end) {
|
|
238
|
+
return null;
|
|
239
|
+
}
|
|
240
|
+
return new Period(start, end, this.precision);
|
|
213
241
|
}
|
|
214
242
|
/**
|
|
215
243
|
* Computes the symmetric difference between this period and another period.
|
|
@@ -261,8 +289,9 @@ class Period {
|
|
|
261
289
|
*/
|
|
262
290
|
renew() {
|
|
263
291
|
const length = this.length();
|
|
264
|
-
const
|
|
265
|
-
const
|
|
292
|
+
const precisionMs = (0, precision_1.getPrecisionInMilliseconds)(this.precision);
|
|
293
|
+
const newStart = new Date(this.endDate.getTime() + precisionMs);
|
|
294
|
+
const newEnd = new Date(newStart.getTime() + length * precisionMs);
|
|
266
295
|
return new Period(newStart, newEnd, this.precision, this.interval);
|
|
267
296
|
}
|
|
268
297
|
/**
|
|
@@ -287,9 +316,14 @@ class Period {
|
|
|
287
316
|
*
|
|
288
317
|
* @param start - The start date, which can be a string or a Date object.
|
|
289
318
|
* @returns The current instance for method chaining.
|
|
319
|
+
* @throws {Error} If the new start date is after the current end date.
|
|
290
320
|
*/
|
|
291
321
|
setStart(start) {
|
|
292
|
-
|
|
322
|
+
const newStartDate = this.validateDate(start);
|
|
323
|
+
if (newStartDate > this.endDate) {
|
|
324
|
+
throw new Error('Start date must be before or equal to end date');
|
|
325
|
+
}
|
|
326
|
+
this.startDate = newStartDate;
|
|
293
327
|
return this;
|
|
294
328
|
}
|
|
295
329
|
/**
|
|
@@ -297,9 +331,14 @@ class Period {
|
|
|
297
331
|
*
|
|
298
332
|
* @param end - The end date as a string or Date object.
|
|
299
333
|
* @returns The current instance for method chaining.
|
|
334
|
+
* @throws {Error} If the new end date is before the current start date.
|
|
300
335
|
*/
|
|
301
336
|
setEnd(end) {
|
|
302
|
-
|
|
337
|
+
const newEndDate = this.validateDate(end);
|
|
338
|
+
if (newEndDate < this.startDate) {
|
|
339
|
+
throw new Error('End date must be after or equal to start date');
|
|
340
|
+
}
|
|
341
|
+
this.endDate = newEndDate;
|
|
303
342
|
return this;
|
|
304
343
|
}
|
|
305
344
|
/**
|
package/dist/utils.d.ts
CHANGED
|
@@ -169,6 +169,7 @@ export declare function formatDate(date: Date, format: string): string;
|
|
|
169
169
|
* @param dateString - The date string to parse.
|
|
170
170
|
* @param format - The format of the date string. Supported format parts are YYYY, MM, DD, HH, mm, ss.
|
|
171
171
|
* @returns A Date object representing the parsed date.
|
|
172
|
+
* @throws {Error} If the date string doesn't match the specified format.
|
|
172
173
|
*
|
|
173
174
|
* @example
|
|
174
175
|
* ```typescript
|
|
@@ -184,5 +185,6 @@ export declare function parseDate(dateString: string, format: string): Date;
|
|
|
184
185
|
* @param end - The ending number of the range.
|
|
185
186
|
* @param step - The step between each number in the range. Defaults to 1.
|
|
186
187
|
* @returns An array of numbers from start to end, incremented by step.
|
|
188
|
+
* @throws {Error} If step is zero or has the wrong sign.
|
|
187
189
|
*/
|
|
188
190
|
export declare function range(start: number, end: number, step?: number): number[];
|
package/dist/utils.js
CHANGED
|
@@ -47,15 +47,14 @@ function getDatesWithInterval(start, end, interval) {
|
|
|
47
47
|
*/
|
|
48
48
|
function getWeeksWithInterval(start, end, interval) {
|
|
49
49
|
const weeks = [];
|
|
50
|
-
|
|
50
|
+
const current = new Date(Date.UTC(start.getUTCFullYear(), start.getUTCMonth(), start.getUTCDate()));
|
|
51
51
|
// Move to the start of the week (Sunday)
|
|
52
52
|
current.setUTCDate(current.getUTCDate() - current.getUTCDay());
|
|
53
|
+
const weekIncrement = Math.max(1, Math.round(interval.getMinutesInterval() / (7 * 24 * 60)));
|
|
53
54
|
while (current <= end) {
|
|
54
55
|
weeks.push(new Date(current));
|
|
55
|
-
// Add
|
|
56
|
-
current
|
|
57
|
-
// Ensure we're at the start of the week
|
|
58
|
-
current.setUTCDate(current.getUTCDate() - current.getUTCDay());
|
|
56
|
+
// Add weeks based on the interval
|
|
57
|
+
current.setUTCDate(current.getUTCDate() + weekIncrement * 7);
|
|
59
58
|
}
|
|
60
59
|
return weeks;
|
|
61
60
|
}
|
|
@@ -72,9 +71,10 @@ function getDaysWithInterval(start, end, interval) {
|
|
|
72
71
|
const days = [];
|
|
73
72
|
const current = new Date(start);
|
|
74
73
|
current.setUTCHours(0, 0, 0, 0);
|
|
74
|
+
const dayIncrement = Math.max(1, Math.round(interval.getMinutesInterval() / (24 * 60)));
|
|
75
75
|
while (current <= end) {
|
|
76
76
|
days.push(new Date(current));
|
|
77
|
-
current.setUTCDate(current.getUTCDate() +
|
|
77
|
+
current.setUTCDate(current.getUTCDate() + dayIncrement);
|
|
78
78
|
}
|
|
79
79
|
return days;
|
|
80
80
|
}
|
|
@@ -91,9 +91,10 @@ function getHoursWithInterval(start, end, interval) {
|
|
|
91
91
|
const hours = [];
|
|
92
92
|
const current = new Date(start);
|
|
93
93
|
current.setMinutes(0, 0, 0);
|
|
94
|
+
const hourIncrement = Math.max(1, Math.round(interval.getMinutesInterval() / 60));
|
|
94
95
|
while (current <= end) {
|
|
95
96
|
hours.push(new Date(current));
|
|
96
|
-
current.setHours(current.getHours() +
|
|
97
|
+
current.setHours(current.getHours() + hourIncrement);
|
|
97
98
|
}
|
|
98
99
|
return hours;
|
|
99
100
|
}
|
|
@@ -110,9 +111,10 @@ function getMinutesWithInterval(start, end, interval) {
|
|
|
110
111
|
const minutes = [];
|
|
111
112
|
const current = new Date(start);
|
|
112
113
|
current.setSeconds(0, 0);
|
|
114
|
+
const minuteIncrement = Math.max(1, interval.getMinutesInterval());
|
|
113
115
|
while (current <= end) {
|
|
114
116
|
minutes.push(new Date(current));
|
|
115
|
-
current.setMinutes(current.getMinutes() +
|
|
117
|
+
current.setMinutes(current.getMinutes() + minuteIncrement);
|
|
116
118
|
}
|
|
117
119
|
return minutes;
|
|
118
120
|
}
|
|
@@ -128,9 +130,11 @@ function getMinutesWithInterval(start, end, interval) {
|
|
|
128
130
|
function getMonthsWithInterval(start, end, interval) {
|
|
129
131
|
const months = [];
|
|
130
132
|
const current = new Date(Date.UTC(start.getUTCFullYear(), start.getUTCMonth(), 1));
|
|
133
|
+
// Use approximation of 30 days per month for interval calculation
|
|
134
|
+
const monthIncrement = Math.max(1, Math.round(interval.getMinutesInterval() / (30 * 24 * 60)));
|
|
131
135
|
while (current <= end) {
|
|
132
136
|
months.push(new Date(current));
|
|
133
|
-
current.setUTCMonth(current.getUTCMonth() +
|
|
137
|
+
current.setUTCMonth(current.getUTCMonth() + monthIncrement);
|
|
134
138
|
}
|
|
135
139
|
return months;
|
|
136
140
|
}
|
|
@@ -146,10 +150,10 @@ function getMonthsWithInterval(start, end, interval) {
|
|
|
146
150
|
function getYearsWithInterval(start, end, interval) {
|
|
147
151
|
const years = [];
|
|
148
152
|
const current = new Date(Date.UTC(start.getUTCFullYear(), 0, 1));
|
|
153
|
+
// Calculate the number of years to add based on the interval
|
|
154
|
+
const yearsToAdd = Math.max(1, Math.round(interval.getMinutesInterval() / 525600)); // 525600 minutes in a year
|
|
149
155
|
while (current <= end) {
|
|
150
156
|
years.push(new Date(current));
|
|
151
|
-
// Calculate the number of years to add based on the interval
|
|
152
|
-
const yearsToAdd = Math.max(1, Math.round(interval.getMinutesInterval() / 525600)); // 525600 minutes in a year
|
|
153
157
|
current.setUTCFullYear(current.getUTCFullYear() + yearsToAdd);
|
|
154
158
|
}
|
|
155
159
|
return years;
|
|
@@ -187,13 +191,20 @@ function addToDate(date, amount, unit) {
|
|
|
187
191
|
break;
|
|
188
192
|
case precision_1.Precision.MONTH:
|
|
189
193
|
newDate.setUTCMonth(newDate.getUTCMonth() + amount);
|
|
194
|
+
// Handle month overflow for dates that don't exist in the target month
|
|
190
195
|
if (newDate.getUTCDate() !== date.getUTCDate()) {
|
|
191
196
|
newDate.setUTCDate(0); // Set to last day of previous month
|
|
192
197
|
}
|
|
193
198
|
break;
|
|
194
199
|
case precision_1.Precision.YEAR:
|
|
195
200
|
newDate.setUTCFullYear(newDate.getUTCFullYear() + amount);
|
|
201
|
+
// Handle leap year edge case (Feb 29 -> Feb 28)
|
|
202
|
+
if (newDate.getUTCMonth() !== date.getUTCMonth()) {
|
|
203
|
+
newDate.setUTCDate(0); // Set to last day of previous month
|
|
204
|
+
}
|
|
196
205
|
break;
|
|
206
|
+
default:
|
|
207
|
+
throw new Error(`Unsupported precision unit: ${unit}`);
|
|
197
208
|
}
|
|
198
209
|
return newDate;
|
|
199
210
|
}
|
|
@@ -291,7 +302,10 @@ function getDaysInMonth(year, month) {
|
|
|
291
302
|
* @returns The formatted date string.
|
|
292
303
|
*/
|
|
293
304
|
function formatDate(date, format) {
|
|
294
|
-
const pad = (n) =>
|
|
305
|
+
const pad = (n) => {
|
|
306
|
+
const str = n.toString();
|
|
307
|
+
return str.length < 2 ? '0' + str : str;
|
|
308
|
+
};
|
|
295
309
|
const map = {
|
|
296
310
|
YYYY: date.getFullYear().toString(),
|
|
297
311
|
MM: pad(date.getMonth() + 1),
|
|
@@ -308,6 +322,7 @@ function formatDate(date, format) {
|
|
|
308
322
|
* @param dateString - The date string to parse.
|
|
309
323
|
* @param format - The format of the date string. Supported format parts are YYYY, MM, DD, HH, mm, ss.
|
|
310
324
|
* @returns A Date object representing the parsed date.
|
|
325
|
+
* @throws {Error} If the date string doesn't match the specified format.
|
|
311
326
|
*
|
|
312
327
|
* @example
|
|
313
328
|
* ```typescript
|
|
@@ -316,13 +331,27 @@ function formatDate(date, format) {
|
|
|
316
331
|
* ```
|
|
317
332
|
*/
|
|
318
333
|
function parseDate(dateString, format) {
|
|
319
|
-
const map = {};
|
|
320
334
|
const formatParts = format.match(/YYYY|MM|DD|HH|mm|ss/gi) || [];
|
|
321
335
|
const dateParts = dateString.match(/\d+/g) || [];
|
|
336
|
+
if (formatParts.length !== dateParts.length) {
|
|
337
|
+
throw new Error(`Date string "${dateString}" does not match the specified format "${format}"`);
|
|
338
|
+
}
|
|
339
|
+
const map = {};
|
|
322
340
|
formatParts.forEach((part, index) => {
|
|
323
|
-
map[part] = parseInt(dateParts[index]);
|
|
341
|
+
map[part] = parseInt(dateParts[index], 10);
|
|
324
342
|
});
|
|
325
|
-
|
|
343
|
+
const year = map['YYYY'] || 0;
|
|
344
|
+
const month = (map['MM'] || 1) - 1; // JavaScript months are 0-indexed
|
|
345
|
+
const day = map['DD'] || 1;
|
|
346
|
+
const hour = map['HH'] || 0;
|
|
347
|
+
const minute = map['mm'] || 0;
|
|
348
|
+
const second = map['ss'] || 0;
|
|
349
|
+
const result = new Date(year, month, day, hour, minute, second);
|
|
350
|
+
// Validate the resulting date
|
|
351
|
+
if (isNaN(result.getTime())) {
|
|
352
|
+
throw new Error(`Invalid date components parsed from "${dateString}"`);
|
|
353
|
+
}
|
|
354
|
+
return result;
|
|
326
355
|
}
|
|
327
356
|
/**
|
|
328
357
|
* Generates an array of numbers within a specified range.
|
|
@@ -331,7 +360,15 @@ function parseDate(dateString, format) {
|
|
|
331
360
|
* @param end - The ending number of the range.
|
|
332
361
|
* @param step - The step between each number in the range. Defaults to 1.
|
|
333
362
|
* @returns An array of numbers from start to end, incremented by step.
|
|
363
|
+
* @throws {Error} If step is zero or has the wrong sign.
|
|
334
364
|
*/
|
|
335
365
|
function range(start, end, step = 1) {
|
|
336
|
-
|
|
366
|
+
if (step === 0) {
|
|
367
|
+
throw new Error('Step cannot be zero');
|
|
368
|
+
}
|
|
369
|
+
if ((end > start && step < 0) || (end < start && step > 0)) {
|
|
370
|
+
throw new Error('Step direction must match the range direction');
|
|
371
|
+
}
|
|
372
|
+
const length = Math.floor(Math.abs(end - start) / Math.abs(step)) + 1;
|
|
373
|
+
return Array.from({ length }, (_, i) => start + i * step);
|
|
337
374
|
}
|
package/package.json
CHANGED