chronos-ts 1.0.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/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+
2
+ The MIT License (MIT)
3
+
4
+ Copyright (c) 2024 Josiah Endurance
5
+
6
+ Permission is hereby granted, free of charge, to any person obtaining a copy
7
+ of this software and associated documentation files (the "Software"), to deal
8
+ in the Software without restriction, including without limitation the rights
9
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10
+ copies of the Software, and to permit persons to whom the Software is
11
+ furnished to do so, subject to the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be included in all
14
+ copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,513 @@
1
+ # Chronos-ts ⏰
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
+
4
+ ## Table of Contents
5
+ - [Installation](#installation)
6
+ - [Overview](#overview)
7
+ - [Usage](#usage)
8
+ - [API Reference](#api-reference)
9
+ - [Classes](#classes)
10
+ - [Period](#period)
11
+ - [Interval](#interval)
12
+ - [Enums](#enums)
13
+ - [Precision](#precision)
14
+ - [Utility Function](#utility-function)
15
+ - [Real-world Scenarios](#real-world-scenarios)
16
+ - [Event Planning and Management](#event-planning-and-management)
17
+ - [Financial Reporting Periods](#financial-reporting-periods)
18
+ - [Employee Leave Management](#employee-leave-management)
19
+ - [Project Timeline Management](#project-timeline-management)
20
+ - [Subscription Billing Cycle Management](#subscription-billing-cycle-management)
21
+ - [Employee Shift Management](#employee-shift-management)
22
+ - [Travel Itinerary Planning](#travel-itinerary-planning)
23
+
24
+ ## Installation
25
+ ```bash
26
+ npm install chronos-ts
27
+
28
+ # or
29
+ yarn add chronos-ts
30
+
31
+ # or
32
+
33
+ pnpm add chronos-ts
34
+ ```
35
+
36
+ ## Overview
37
+ This package provides an easy-to-use API for working with periods and intervals, along with precise handling of date and time logic. Inspired by the [spatie/period](https://github.com/spatie/period) package in PHP, this package provides support for both TypeScript and JavaScript environments, offering advanced time manipulation. It offers a user-friendly API for working with periods and intervals, providing precise date and time logic handling. Key features include:
38
+ - Flexible period creation and manipulation with customizable precision (minute to year)
39
+ - Interval generation for recurring events (e.g., "every 3 days")
40
+ - Comprehensive period comparisons (overlaps, contains, adjacent)
41
+ - Precise start/end date handling with various time units
42
+ - Advanced operations like symmetric differences and unions between periods
43
+ - Adjacent period detection for seamless time range management
44
+ - Extensive utility functions for common date operations and formatting
45
+
46
+ Whether you're building scheduling systems, financial applications, or any project requiring sophisticated time calculations, Chronos simplifies complex time-based operations in both TypeScript and JavaScript environments.
47
+
48
+
49
+ ## Usage
50
+ ### Creating and Manipulating Periods
51
+ ````typescript
52
+ import { Period, Precision, Interval } from 'chronos-ts';
53
+
54
+ // Create a period for the year 2023
55
+ const year2023 = new Period('2023-01-01', '2023-12-31', Precision.DAY);
56
+
57
+ // Check if a date is within the period
58
+ const isInPeriod = year2023.contains('2023-06-15'); // true
59
+
60
+ // Create a period for Q2 2023
61
+ const q2_2023 = new Period('2023-04-01', '2023-06-30', Precision.DAY);
62
+
63
+ // Check if periods overlap
64
+ const periodsOverlap = year2023.overlapsWith(q2_2023); // true
65
+
66
+ // Get the overlapping period
67
+ const overlap = year2023.overlap(q2_2023);
68
+ console.log(overlap?.startDate, overlap?.endDate); // 2023-04-01, 2023-06-30
69
+
70
+ // Subtract a period
71
+ const remainingPeriods = year2023.subtract(q2_2023);
72
+ console.log(remainingPeriods.length); // 2
73
+ console.log(remainingPeriods[0].startDate, remainingPeriods[0].endDate); // 2023-01-01, 2023-03-31
74
+ console.log(remainingPeriods[1].startDate, remainingPeriods[1].endDate); // 2023-07-01, 2023-12-31
75
+
76
+ // Create a period with an interval
77
+ const weeklyPeriod = new Period('2023-01-01', '2023-12-31', Precision.WEEK, Interval.weeks(1));
78
+
79
+ // Get dates in the interval
80
+ const weeklyDates = weeklyPeriod.getDatesInInterval();
81
+ console.log(weeklyDates?.length); // 53 (number of weeks in 2023)
82
+
83
+ // Renew a period
84
+ const nextYear = year2023.renew();
85
+ console.log(nextYear.startDate, nextYear.endDate); // 2024-01-01, 2024-12-31
86
+
87
+ // Use fluent API
88
+ const customPeriod = new Period('2023-01-01', '2023-12-31')
89
+ .setPrecision(Precision.MONTH)
90
+ .setInterval(Interval.months(3));
91
+
92
+ console.log(customPeriod.getDatesInInterval()?.length); // 5 (Jan, Apr, Jul, Oct, Jan)
93
+ ````
94
+
95
+ ### Working with Intervals
96
+ ``` typescript
97
+ import { Interval } from 'chronos-ts';
98
+
99
+ const twoHours = Interval.hours(2);
100
+ console.log(twoHours.getMinutesInterval()); // 120
101
+
102
+ const threeWeeks = Interval.weeks(3);
103
+ console.log(threeWeeks.getMinutesInterval()); // 30240 (3 * 7 * 24 * 60)
104
+
105
+ const customInterval = new Interval(30, 2, 1, 0, 1); // 30 minutes, 2 hours, 1 day, 0 weeks, 1 month
106
+ console.log(customInterval.getMinutesInterval()); // 44310 (30 + 120 + 1440 + 43200)
107
+ ```
108
+ ### Using Utility Functions
109
+ ```typescript
110
+ import { addToDate, subtractFromDate, formatDate, parseDate, getWeekNumber, isLeapYear, Precision } from 'chronos-ts';
111
+
112
+ const today = new Date();
113
+
114
+ // Add 3 months to today
115
+ const futureDate = addToDate(today, 3, Precision.MONTH);
116
+
117
+ // Subtract 2 weeks from today
118
+ const pastDate = subtractFromDate(today, 2, Precision.WEEK);
119
+
120
+ // Format a date
121
+ const formattedDate = formatDate(today, 'YYYY-MM-DD HH:mm:ss');
122
+
123
+ // Parse a date string
124
+ const parsedDate = parseDate('2023-06-15 14:30:00', 'YYYY-MM-DD HH:mm:ss');
125
+
126
+ // Get the week number
127
+ const weekNumber = getWeekNumber(today);
128
+
129
+ // Check if it's a leap year
130
+ const isLeap = isLeapYear(2024); // true
131
+ ```
132
+
133
+
134
+
135
+ ## API Reference
136
+ ### Classes
137
+ #### Period
138
+ The `Period` class represents a time period with a start date, end date, precision, and optional interval.
139
+
140
+ ##### Constructor
141
+ ```typescript
142
+ constructor(start: string | Date, end: string | Date, precision: Precision = Precision.DAY, interval: Interval | null = null)
143
+ ```
144
+
145
+ ##### Methods
146
+ - `contains(date: string | Date): boolean`: Checks if the given date is within the period.
147
+ - `overlapsWith(other: Period): boolean`: Checks if this period overlaps with another period.
148
+ - `isAdjacentTo(other: Period): boolean`: Checks if this period is adjacent to another period.
149
+ - `getDatesInInterval(): Date[] | null`: Returns an array of dates within the period based on the interval.
150
+ - `getMinutesInInterval(): number`: Returns the number of minutes in the period.
151
+ - `getHoursInInterval(): number`: Returns the number of hours in the period.
152
+ - `getDaysInInterval(): number`: Returns the number of days in the period.
153
+ - `getWeeksInInterval(): number`: Returns the number of weeks in the period.
154
+ - `getMonthsInInterval(): number`: Returns the number of months in the period.
155
+ - `getYearsInInterval(): number`: Returns the number of years in the period.
156
+ - `length(): number`: Returns the length of the period in the specified precision.
157
+ - `overlap(other: Period): Period | null`: Returns the overlapping period with another period, if any.
158
+ - `subtract(other: Period): Period[]`: Subtracts another period from this period.
159
+ - `gap(other: Period): Period | null`: Returns the gap between this period and another period, if any.
160
+ - `symmetricDifference(other: Period): Period[]`: Returns the symmetric difference between this period and another period.
161
+ - `renew(): Period`: Creates a new period of the same length immediately following this period.
162
+ - `union(other: Period): Period[]`: Returns the union of this period with another period.
163
+
164
+ ##### Fluent API Methods
165
+
166
+ `setStart(start: string | Date): this`: Sets the start date of the period.
167
+ `setEnd(end: string | Date): this`: Sets the end date of the period.
168
+ `setPrecision(precision: Precision): this`: Sets the precision of the period.
169
+ `setInterval(interval: Interval): this`: Sets the interval of the period.
170
+
171
+ #### Interval
172
+ The `Interval` class represents a time interval with minutes, hours, days, weeks, and months.
173
+ Static Methods
174
+
175
+ - `minutes(minutes: number): Interval:` Creates an interval with the specified number of minutes.
176
+ - `hours(hours: number): Interval`: Creates an interval with the specified number of hours.
177
+ - `days(days: number): Interval`: Creates an interval with the specified number of days.
178
+ - `weeks(weeks: number): Interval`: Creates an interval with the specified number of weeks.
179
+ - `months(months: number): Interval`: Creates an interval with the specified number of months.
180
+
181
+ ##### Methods
182
+
183
+ `getMinutesInterval(): number`: Returns the total number of minutes in the interval.
184
+
185
+
186
+ #### Enums
187
+ ##### Precision
188
+ The Precision enum represents different levels of time precision.
189
+ typescriptCopyenum Precision {
190
+ MINUTE = 'minute',
191
+ HOUR = 'hour',
192
+ DAY = 'day',
193
+ WEEK = 'week',
194
+ MONTH = 'month',
195
+ YEAR = 'year',
196
+ }
197
+
198
+ ### Utility Function
199
+ The package includes various utility functions for working with dates:
200
+
201
+ - `getDatesWithInterval(start: Date, end: Date, interval: Interval): Date[]` - Returns an array of dates within the specified interval.
202
+ - `getWeeksWithInterval(start: Date, end: Date, interval: Interval): Date[]` - Returns an array of weeks within the specified interval.
203
+ - `getDaysWithInterval(start: Date, end: Date, interval: Interval): Date[]` - Returns an array of days within the specified interval.
204
+ - `getHoursWithInterval(start: Date, end: Date, interval: Interval): Date[]` - Returns an array of hours within the specified interval.
205
+ - `getMinutesWithInterval(start: Date, end: Date, interval: Interval): Date[]` - Returns an array of minutes within the specified interval.
206
+ - `getMonthsWithInterval(start: Date, end: Date, interval: Interval): Date[]` - Returns an array of months within the specified interval.
207
+ - `getYearsWithInterval(start: Date, end: Date, interval: Interval): Date[]` - Returns an array of years within the specified interval.
208
+ - `addToDate(date: Date, amount: number, unit: Precision): Date` - Adds the specified amount of time to a date.
209
+ - `subtractFromDate(date: Date, amount: number, unit: Precision): Date` - Subtracts the specified amount of time from a date.
210
+ - `isSameDay(date1: Date, date2: Date): boolean` - Checks if two dates are on the same day.
211
+ - `getWeekNumber(date: Date): number` - Returns the week number of a date.
212
+ - `getQuarter(date: Date): number` - Returns the quarter of a date.
213
+ - `isLeapYear(year: number): boolean` - Checks if a year is a leap year.
214
+ - `getDaysInMonth(year: number, month: number): number` - Returns the number of days in a month.
215
+ - `formatDate(date: Date, format: string): string`
216
+ - `parseDate(dateString: string, format: string): Date` - Parses a date string using the specified format.
217
+ - `range(start: number, end: number, step: number = 1): number[]` - Returns an array of numbers within the specified range.
218
+
219
+ ### Real-world Scenarios
220
+ **1. Event Planning and Management**
221
+ The `Period` class can be used to represent events, while the `Interval` class can help manage recurring events.
222
+ ``` typescript
223
+ import { Period, Interval, Precision } from 'chronos-ts';
224
+
225
+ // Create an event
226
+ const conference = new Period('2023-09-15 09:00', '2023-09-17 18:00', Precision.HOUR);
227
+
228
+ // Check if a specific time is during the conference
229
+ const isDuringConference = conference.contains('2023-09-16 14:30'); // true
230
+
231
+ // Create a recurring weekly meeting
232
+ const weeklyMeeting = new Period('2023-01-01 10:00', '2023-12-31 11:00', Precision.HOUR, Interval.weeks(1));
233
+
234
+ // Get all meeting dates
235
+ const meetingDates = weeklyMeeting.getDatesInInterval();
236
+
237
+ // Check for conflicts with the conference
238
+ const conflictingMeetings = meetingDates?.filter(date => conference.contains(date)) || [];
239
+ console.log(`There are ${conflictingMeetings.length} conflicting meetings during the conference.`);
240
+ ```
241
+
242
+ **2. Financial Reporting Periods**
243
+ Use the `Period` class to represent financial quarters and calculate year-to-date periods.
244
+ ``` typescript
245
+ import { Period, Precision } from 'chronos-ts';
246
+
247
+ const q1_2023 = new Period('2023-01-01', '2023-03-31', Precision.DAY);
248
+ const q2_2023 = new Period('2023-04-01', '2023-06-30', Precision.DAY);
249
+ const q3_2023 = new Period('2023-07-01', '2023-09-30', Precision.DAY);
250
+ const q4_2023 = new Period('2023-10-01', '2023-12-31', Precision.DAY);
251
+
252
+ // Calculate Year-to-Date period
253
+ const ytd = (currentQuarter: Period): Period => {
254
+ return new Period('2023-01-01', currentQuarter.endDate, Precision.DAY);
255
+ };
256
+
257
+ const q3YTD = ytd(q3_2023);
258
+ console.log(`Q3 YTD period: ${q3YTD.startDate.toDateString()} - ${q3YTD.endDate.toDateString()}`);
259
+
260
+ // Calculate quarter-over-quarter growth
261
+ const calculateQoQGrowth = (currentQuarter: number, previousQuarter: number): string => {
262
+ const growth = (currentQuarter - previousQuarter) / previousQuarter * 100;
263
+ return `${growth.toFixed(2)}%`;
264
+ };
265
+
266
+ const q2Revenue = 1000000;
267
+ const q3Revenue = 1200000;
268
+ console.log(`Q3 QoQ Growth: ${calculateQoQGrowth(q3Revenue, q2Revenue)}`);
269
+ ```
270
+
271
+ **3. Employee Leave Management**
272
+ Use the `Period` class to manage employee leave requests and calculate leave balances.
273
+ ``` typescript
274
+ import { Period, Precision } from 'chronos-ts';
275
+
276
+ // Create a leave request period
277
+ const leaveRequest = new Period('2023-09-15', '2023-09-17', Precision.DAY);
278
+
279
+ // Check if the leave request overlaps with existing leave periods
280
+ const existingLeaves = [
281
+ new Period('2023-09-10', '2023-09-14', Precision.DAY),
282
+ new Period('2023-09-18', '2023-09-20', Precision.DAY),
283
+ ];
284
+
285
+ const overlappingLeaves = existingLeaves.filter(leave => leaveRequest.overlapsWith(leave));
286
+ console.log(`There are ${overlappingLeaves.length} overlapping leave requests.`);
287
+
288
+ // Calculate remaining leave balance
289
+ const totalLeaveDays = 20;
290
+ const usedLeaveDays = existingLeaves.reduce((total, leave) => total + leave.length(), 0);
291
+ const remainingLeaveDays = totalLeaveDays - usedLeaveDays;
292
+ console.log(`Remaining leave days: ${remainingLeaveDays}`);
293
+
294
+ // Renew leave balance for the next year
295
+ const nextYearLeaveBalance = remainingLeaveDays > 0 ? new Period('2024-01-01', '2024-12-31') : null;
296
+ console.log(`Next year's leave balance: ${nextYearLeaveBalance ? nextYearLeaveBalance.length() : 0} days`);
297
+
298
+ ```
299
+
300
+ **4. Project Timeline Management**
301
+ Use the Period class to manage project timelines and track overlapping tasks.
302
+ ``` typescript
303
+ import { Period, Precision } from 'chronos-ts';
304
+
305
+ const projectTimeline = new Period('2023-01-01', '2023-12-31', Precision.DAY);
306
+
307
+ const tasks = [
308
+ new Period('2023-01-15', '2023-03-31', Precision.DAY),
309
+ new Period('2023-03-01', '2023-05-15', Precision.DAY),
310
+ new Period('2023-05-01', '2023-08-31', Precision.DAY),
311
+ new Period('2023-08-15', '2023-11-30', Precision.DAY),
312
+ ];
313
+
314
+ // Find overlapping tasks
315
+ const findOverlappingTasks = (tasks: Period[]): [Period, Period][] => {
316
+ const overlaps: [Period, Period][] = [];
317
+ for (let i = 0; i < tasks.length; i++) {
318
+ for (let j = i + 1; j < tasks.length; j++) {
319
+ if (tasks[i].overlapsWith(tasks[j])) {
320
+ overlaps.push([tasks[i], tasks[j]]);
321
+ }
322
+ }
323
+ }
324
+ return overlaps;
325
+ };
326
+
327
+ const overlappingTasks = findOverlappingTasks(tasks);
328
+ console.log(`There are ${overlappingTasks.length} overlapping tasks in the project.`);
329
+
330
+ // Calculate project progress
331
+ const calculateProgress = (currentDate: Date): number => {
332
+ const daysPassed = projectTimeline.startDate.getMinutesInInterval() / (24 * 60);
333
+ const totalDays = projectTimeline.getDaysInInterval();
334
+ return (daysPassed / totalDays) * 100;
335
+ };
336
+
337
+ const currentProgress = calculateProgress(new Date('2023-06-15'));
338
+ console.log(`Project progress: ${currentProgress.toFixed(2)}%`);
339
+ ```
340
+
341
+ **5. Subscription Billing Cycle Management**
342
+ Use the Period class to manage subscription periods and calculate renewal dates.
343
+ ``` typescript
344
+ import { Period, Precision, addToDate } from 'chronos-ts';
345
+
346
+ class Subscription {
347
+ constructor(public startDate: Date, public plan: 'monthly' | 'annual') {}
348
+
349
+ getCurrentPeriod(): Period {
350
+ const endDate = addToDate(this.startDate, 1, this.plan === 'monthly' ? Precision.MONTH : Precision.YEAR);
351
+ return new Period(this.startDate, endDate, Precision.DAY);
352
+ }
353
+
354
+ isActive(date: Date = new Date()): boolean {
355
+ return this.getCurrentPeriod().contains(date);
356
+ }
357
+
358
+ getRenewalDate(): Date {
359
+ return this.getCurrentPeriod().endDate;
360
+ }
361
+
362
+ renew(): void {
363
+ this.startDate = this.getRenewalDate();
364
+ }
365
+ }
366
+
367
+ const monthlySubscription = new Subscription(new Date('2023-01-01'), 'monthly');
368
+ console.log(`Monthly subscription active: ${monthlySubscription.isActive()}`);
369
+ console.log(`Monthly subscription renewal date: ${monthlySubscription.getRenewalDate().toDateString()}`);
370
+
371
+ const annualSubscription = new Subscription(new Date('2023-01-01'), 'annual');
372
+ console.log(`Annual subscription active: ${annualSubscription.isActive()}`);
373
+ console.log(`Annual subscription renewal date: ${annualSubscription.getRenewalDate().toDateString()}`);
374
+
375
+ const annualSubscription = new Subscription(new Date('2023-01-01'), 'annual');
376
+ console.log(`Annual subscription active: ${annualSubscription.isActive()}`);
377
+ console.log(`Annual subscription renewal date: ${annualSubscription.getRenewalDate().toDateString()}`);
378
+ // Check if a subscription will be active on a future date
379
+ const futureDate = new Date('2023-12-15');
380
+ console.log(`Monthly subscription active on ${futureDate.toDateString()}: ${monthlySubscription.isActive(futureDate)}`);
381
+ console.log(`Annual subscription active on ${futureDate.toDateString()}: ${annualSubscription.isActive(futureDate)}`);
382
+ // Renew a subscription
383
+ monthlySubscription.renew();
384
+ console.log(`Monthly subscription renewed. New period: ${monthlySubscription.getCurrentPeriod().startDate.toDateString()} - ${monthlySubscription.getCurrentPeriod().endDate.toDateString()})`;
385
+ ```
386
+
387
+ **6. Employee Shift Management**
388
+
389
+ Use the `Period` and `Interval` classes to manage employee shifts and calculate overtime.
390
+
391
+ ```typescript
392
+ import { Period, Interval, Precision, addToDate } from 'chronos-ts';
393
+
394
+ class Shift extends Period {
395
+ constructor(public employee: string, start: Date, end: Date) {
396
+ super(start, end, Precision.MINUTE);
397
+ }
398
+
399
+ getDuration(): number {
400
+ return this.getHoursInInterval();
401
+ }
402
+
403
+ isOvertime(): boolean {
404
+ return this.getDuration() > 8;
405
+ }
406
+
407
+ getOvertimeHours(): number {
408
+ return Math.max(0, this.getDuration() - 8);
409
+ }
410
+ }
411
+
412
+ // Create a week's worth of shifts for an employee
413
+ const createWeekShifts = (employee: string, startDate: Date): Shift[] => {
414
+ const shifts: Shift[] = [];
415
+ for (let i = 0; i < 5; i++) { // Assuming 5-day work week
416
+ const shiftStart = addToDate(startDate, i, Precision.DAY);
417
+ shiftStart.setHours(9, 0, 0, 0); // 9 AM start
418
+ const shiftEnd = new Date(shiftStart);
419
+ shiftEnd.setHours(17, 0, 0, 0); // 5 PM end
420
+ shifts.push(new Shift(employee, shiftStart, shiftEnd));
421
+ }
422
+ return shifts;
423
+ };
424
+
425
+ const employeeShifts = createWeekShifts('John Doe', new Date('2023-06-19'));
426
+
427
+ // Calculate total hours worked and overtime
428
+ const totalHours = employeeShifts.reduce((sum, shift) => sum + shift.getDuration(), 0);
429
+ const overtimeHours = employeeShifts.reduce((sum, shift) => sum + shift.getOvertimeHours(), 0);
430
+
431
+ console.log(`Total hours worked: ${totalHours}`);
432
+ console.log(`Overtime hours: ${overtimeHours}`);
433
+
434
+ // Check for conflicting shifts
435
+ const hasConflict = (shifts: Shift[]): boolean => {
436
+ for (let i = 0; i < shifts.length; i++) {
437
+ for (let j = i + 1; j < shifts.length; j++) {
438
+ if (shifts[i].overlapsWith(shifts[j])) {
439
+ return true;
440
+ }
441
+ }
442
+ }
443
+ return false;
444
+ };
445
+
446
+ console.log(`Shifts have conflicts: ${hasConflict(employeeShifts)}`);
447
+ ```
448
+
449
+ **7. Travel Itinerary Planning**
450
+ Use the `Period` class to manage travel itineraries and check for scheduling conflicts.
451
+ ``` typescript
452
+ import { Period, Precision, addToDate } from 'chronos-ts';
453
+
454
+ class TravelEvent extends Period {
455
+ constructor(public description: string, start: Date, end: Date) {
456
+ super(start, end, Precision.MINUTE);
457
+ }
458
+ }
459
+
460
+ class Itinerary {
461
+ events: TravelEvent[] = [];
462
+
463
+ addEvent(event: TravelEvent): void {
464
+ if (this.hasConflict(event)) {
465
+ throw new Error('Event conflicts with existing events');
466
+ }
467
+ this.events.push(event);
468
+ }
469
+
470
+ hasConflict(newEvent: TravelEvent): boolean {
471
+ return this.events.some(event => event.overlapsWith(newEvent));
472
+ }
473
+
474
+ getDuration(): number {
475
+ if (this.events.length === 0) return 0;
476
+ const start = this.events.reduce((min, e) => e.startDate < min ? e.startDate : min, this.events[0].startDate);
477
+ const end = this.events.reduce((max, e) => e.endDate > max ? e.endDate : max, this.events[0].endDate);
478
+ return new Period(start, end, Precision.HOUR).getDaysInInterval();
479
+ }
480
+ }
481
+
482
+ // Create a travel itinerary
483
+ const itinerary = new Itinerary();
484
+
485
+ // Add events to the itinerary
486
+ try {
487
+ itinerary.addEvent(new TravelEvent('Flight to Paris', new Date('2023-07-01 10:00'), new Date('2023-07-01 12:00')));
488
+ itinerary.addEvent(new TravelEvent('Hotel Check-in', new Date('2023-07-01 14:00'), new Date('2023-07-01 15:00')));
489
+ itinerary.addEvent(new TravelEvent('Eiffel Tower Visit', new Date('2023-07-02 10:00'), new Date('2023-07-02 13:00')));
490
+ itinerary.addEvent(new TravelEvent('Louvre Museum', new Date('2023-07-03 09:00'), new Date('2023-07-03 12:00')));
491
+ itinerary.addEvent(new TravelEvent('Flight to Rome', new Date('2023-07-04 15:00'), new Date('2023-07-04 17:00')));
492
+ } catch (error) {
493
+ console.error('Error creating itinerary:', error.message);
494
+ }
495
+
496
+ console.log(`Itinerary duration: ${itinerary.getDuration()} days`);
497
+
498
+ // Try to add a conflicting event
499
+ try {
500
+ itinerary.addEvent(new TravelEvent('Conflicting Event', new Date('2023-07-01 11:00'), new Date('2023-07-01 13:00')));
501
+ } catch (error) {
502
+ console.log('Caught conflicting event:', error.message);
503
+ }
504
+ ```
505
+
506
+ ## Contributing 🤝
507
+ Contributions, issues and feature requests are welcome. After cloning & setting up project locally, you can just submit a PR to this repo and it will be deployed once it's accepted. There is a list of TODOs in the [TODO.md](TODO.md) file. The project is still in its early stages, so there are plenty of opportunities to contribute.
508
+
509
+ ## License 📝
510
+ This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
511
+
512
+ ## Acknowledgements 🙏
513
+ - [spatie/period]((https://github.com/spatie/period)
@@ -0,0 +1,3 @@
1
+ export { Period } from './period';
2
+ export { Precision } from './precision';
3
+ export { Interval } from './interval';
package/dist/index.js ADDED
@@ -0,0 +1,9 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.Interval = exports.Precision = exports.Period = void 0;
4
+ var period_1 = require("./period");
5
+ Object.defineProperty(exports, "Period", { enumerable: true, get: function () { return period_1.Period; } });
6
+ var precision_1 = require("./precision");
7
+ Object.defineProperty(exports, "Precision", { enumerable: true, get: function () { return precision_1.Precision; } });
8
+ var interval_1 = require("./interval");
9
+ Object.defineProperty(exports, "Interval", { enumerable: true, get: function () { return interval_1.Interval; } });
@@ -0,0 +1,14 @@
1
+ export declare class Interval {
2
+ private minutes;
3
+ private hours;
4
+ private days;
5
+ private weeks;
6
+ private months;
7
+ private constructor();
8
+ static minutes(minutes: number): Interval;
9
+ static hours(hours: number): Interval;
10
+ static days(days: number): Interval;
11
+ static weeks(weeks: number): Interval;
12
+ static months(months: number): Interval;
13
+ getMinutesInterval(): number;
14
+ }
@@ -0,0 +1,35 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.Interval = void 0;
4
+ class Interval {
5
+ constructor(minutes = 0, hours = 0, days = 0, weeks = 0, months = 0) {
6
+ this.minutes = minutes;
7
+ this.hours = hours;
8
+ this.days = days;
9
+ this.weeks = weeks;
10
+ this.months = months;
11
+ }
12
+ static minutes(minutes) {
13
+ return new Interval(minutes);
14
+ }
15
+ static hours(hours) {
16
+ return new Interval(0, hours);
17
+ }
18
+ static days(days) {
19
+ return new Interval(0, 0, days);
20
+ }
21
+ static weeks(weeks) {
22
+ return new Interval(0, 0, 0, weeks);
23
+ }
24
+ static months(months) {
25
+ return new Interval(0, 0, 0, 0, months);
26
+ }
27
+ getMinutesInterval() {
28
+ return (this.minutes +
29
+ this.hours * 60 +
30
+ this.days * 24 * 60 +
31
+ this.weeks * 7 * 24 * 60 +
32
+ this.months * 30 * 24 * 60);
33
+ }
34
+ }
35
+ exports.Interval = Interval;
@@ -0,0 +1,31 @@
1
+ import { Interval } from './interval';
2
+ import { Precision } from './precision';
3
+ export declare class Period {
4
+ private startDate;
5
+ private endDate;
6
+ private precision;
7
+ private interval;
8
+ constructor(start: string | Date, end: string | Date, precision?: Precision, interval?: Interval | null);
9
+ contains(date: string | Date): boolean;
10
+ overlapsWith(other: Period): boolean;
11
+ isAdjacentTo(other: Period): boolean;
12
+ getDatesInInterval(): Date[] | null;
13
+ getMinutesInInterval(): number;
14
+ getHoursInInterval(): number;
15
+ getDaysInInterval(): number;
16
+ getWeeksInInterval(): number;
17
+ getMonthsInInterval(): number;
18
+ getYearsInInterval(): number;
19
+ length(): number;
20
+ overlap(other: Period): Period | null;
21
+ subtract(other: Period): Period[];
22
+ gap(other: Period): Period | null;
23
+ symmetricDifference(other: Period): Period[];
24
+ private mergeAdjacentPeriods;
25
+ renew(): Period;
26
+ union(other: Period): Period[];
27
+ setStart(start: string | Date): this;
28
+ setEnd(end: string | Date): this;
29
+ setPrecision(precision: Precision): this;
30
+ setInterval(interval: Interval): this;
31
+ }
package/dist/period.js ADDED
@@ -0,0 +1,168 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.Period = void 0;
4
+ const precision_1 = require("./precision");
5
+ const utils_1 = require("./utils");
6
+ class Period {
7
+ constructor(start, end, precision = precision_1.Precision.DAY, interval = null) {
8
+ this.startDate = new Date(start);
9
+ this.endDate = new Date(end);
10
+ this.precision = precision;
11
+ this.interval = interval;
12
+ }
13
+ contains(date) {
14
+ const checkDate = new Date(date);
15
+ return checkDate >= this.startDate && checkDate <= this.endDate;
16
+ }
17
+ overlapsWith(other) {
18
+ return this.startDate < other.endDate && this.endDate > other.startDate;
19
+ }
20
+ isAdjacentTo(other) {
21
+ const thisPrecisionMs = (0, precision_1.getPrecisionInMilliseconds)(this.precision);
22
+ const otherPrecisionMs = (0, precision_1.getPrecisionInMilliseconds)(other.precision);
23
+ const maxPrecisionMs = Math.max(thisPrecisionMs, otherPrecisionMs);
24
+ const thisEndTime = this.endDate.getTime();
25
+ const otherStartTime = other.startDate.getTime();
26
+ const thisStartTime = this.startDate.getTime();
27
+ const otherEndTime = other.endDate.getTime();
28
+ // Check for overlap
29
+ if (thisStartTime <= otherEndTime && otherStartTime <= thisEndTime) {
30
+ return false;
31
+ }
32
+ // Check if the gap between periods is within one precision unit
33
+ const gap = Math.abs(otherStartTime - thisEndTime);
34
+ const reverseGap = Math.abs(thisStartTime - otherEndTime);
35
+ return ((gap > 0 && gap <= maxPrecisionMs) ||
36
+ (reverseGap > 0 && reverseGap <= maxPrecisionMs));
37
+ }
38
+ getDatesInInterval() {
39
+ if (this.interval) {
40
+ return (0, utils_1.getDatesWithInterval)(this.startDate, this.endDate, this.interval);
41
+ }
42
+ return null;
43
+ }
44
+ getMinutesInInterval() {
45
+ return Math.floor((this.endDate.getTime() - this.startDate.getTime()) / (1000 * 60));
46
+ }
47
+ getHoursInInterval() {
48
+ return Math.floor(this.getMinutesInInterval() / 60);
49
+ }
50
+ getDaysInInterval() {
51
+ return Math.floor(this.getHoursInInterval() / 24);
52
+ }
53
+ getWeeksInInterval() {
54
+ return Math.floor(this.getDaysInInterval() / 7);
55
+ }
56
+ getMonthsInInterval() {
57
+ let months = (this.endDate.getFullYear() - this.startDate.getFullYear()) * 12;
58
+ months += this.endDate.getMonth() - this.startDate.getMonth();
59
+ // Adjust for month boundaries
60
+ if (this.endDate.getDate() < this.startDate.getDate()) {
61
+ months--;
62
+ }
63
+ return months;
64
+ }
65
+ getYearsInInterval() {
66
+ return Math.floor(this.getMonthsInInterval() / 12);
67
+ }
68
+ length() {
69
+ return Math.floor((this.endDate.getTime() - this.startDate.getTime()) /
70
+ (0, precision_1.getPrecisionInMilliseconds)(this.precision));
71
+ }
72
+ overlap(other) {
73
+ if (!this.overlapsWith(other)) {
74
+ return null;
75
+ }
76
+ const start = new Date(Math.max(this.startDate.getTime(), other.startDate.getTime()));
77
+ const end = new Date(Math.min(this.endDate.getTime(), other.endDate.getTime()));
78
+ return new Period(start, end, this.precision);
79
+ }
80
+ subtract(other) {
81
+ if (!this.overlapsWith(other)) {
82
+ return [this];
83
+ }
84
+ const periods = [];
85
+ if (this.startDate < other.startDate) {
86
+ const endDate = new Date(other.startDate.getTime() - 1);
87
+ periods.push(new Period(this.startDate, endDate, this.precision));
88
+ }
89
+ if (this.endDate > other.endDate) {
90
+ const startDate = new Date(other.endDate.getTime() + 1);
91
+ periods.push(new Period(startDate, this.endDate, this.precision));
92
+ }
93
+ return periods;
94
+ }
95
+ gap(other) {
96
+ if (this.overlapsWith(other) || this.isAdjacentTo(other)) {
97
+ return null;
98
+ }
99
+ const precisionMs = (0, precision_1.getPrecisionInMilliseconds)(this.precision);
100
+ const start = this.endDate < other.startDate ? this.endDate : other.endDate;
101
+ const end = this.startDate > other.startDate ? this.startDate : other.startDate;
102
+ return new Period(new Date(start.getTime() + precisionMs), new Date(end.getTime() - precisionMs), this.precision);
103
+ }
104
+ symmetricDifference(other) {
105
+ const periods = [];
106
+ const overlapPeriod = this.overlap(other);
107
+ if (!overlapPeriod) {
108
+ // No overlap, return both periods
109
+ return [this, other].sort((a, b) => a.startDate.getTime() - b.startDate.getTime());
110
+ }
111
+ const thisSubtracted = this.subtract(overlapPeriod);
112
+ const otherSubtracted = other.subtract(overlapPeriod);
113
+ periods.push(...thisSubtracted, ...otherSubtracted);
114
+ // Sort periods and merge adjacent ones
115
+ return this.mergeAdjacentPeriods(periods);
116
+ }
117
+ mergeAdjacentPeriods(periods) {
118
+ if (periods.length <= 1)
119
+ return periods;
120
+ periods.sort((a, b) => a.startDate.getTime() - b.startDate.getTime());
121
+ const merged = [periods[0]];
122
+ for (let i = 1; i < periods.length; i++) {
123
+ const current = periods[i];
124
+ const previous = merged[merged.length - 1];
125
+ if (previous.isAdjacentTo(current) || previous.overlapsWith(current)) {
126
+ merged[merged.length - 1] = new Period(previous.startDate, current.endDate > previous.endDate
127
+ ? current.endDate
128
+ : previous.endDate, this.precision);
129
+ }
130
+ else {
131
+ merged.push(current);
132
+ }
133
+ }
134
+ return merged;
135
+ }
136
+ renew() {
137
+ const length = this.length();
138
+ const newStart = new Date(this.endDate.getTime() + (0, precision_1.getPrecisionInMilliseconds)(this.precision));
139
+ const newEnd = new Date(newStart.getTime() + length * (0, precision_1.getPrecisionInMilliseconds)(this.precision));
140
+ return new Period(newStart, newEnd, this.precision, this.interval);
141
+ }
142
+ union(other) {
143
+ if (this.overlapsWith(other) || this.isAdjacentTo(other)) {
144
+ const start = new Date(Math.min(this.startDate.getTime(), other.startDate.getTime()));
145
+ const end = new Date(Math.max(this.endDate.getTime(), other.endDate.getTime()));
146
+ return [new Period(start, end, this.precision)];
147
+ }
148
+ return [this, other].sort((a, b) => a.startDate.getTime() - b.startDate.getTime());
149
+ }
150
+ // Fluent API methods
151
+ setStart(start) {
152
+ this.startDate = new Date(start);
153
+ return this;
154
+ }
155
+ setEnd(end) {
156
+ this.endDate = new Date(end);
157
+ return this;
158
+ }
159
+ setPrecision(precision) {
160
+ this.precision = precision;
161
+ return this;
162
+ }
163
+ setInterval(interval) {
164
+ this.interval = interval;
165
+ return this;
166
+ }
167
+ }
168
+ exports.Period = Period;
@@ -0,0 +1,9 @@
1
+ export declare enum Precision {
2
+ MINUTE = "minute",
3
+ HOUR = "hour",
4
+ DAY = "day",
5
+ WEEK = "week",
6
+ MONTH = "month",
7
+ YEAR = "year"
8
+ }
9
+ export declare function getPrecisionInMilliseconds(precision: Precision): number;
@@ -0,0 +1,31 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.Precision = void 0;
4
+ exports.getPrecisionInMilliseconds = getPrecisionInMilliseconds;
5
+ var Precision;
6
+ (function (Precision) {
7
+ Precision["MINUTE"] = "minute";
8
+ Precision["HOUR"] = "hour";
9
+ Precision["DAY"] = "day";
10
+ Precision["WEEK"] = "week";
11
+ Precision["MONTH"] = "month";
12
+ Precision["YEAR"] = "year";
13
+ })(Precision || (exports.Precision = Precision = {}));
14
+ function getPrecisionInMilliseconds(precision) {
15
+ switch (precision) {
16
+ case Precision.MINUTE:
17
+ return 1000 * 60;
18
+ case Precision.HOUR:
19
+ return 1000 * 60 * 60;
20
+ case Precision.DAY:
21
+ return 1000 * 60 * 60 * 24;
22
+ case Precision.WEEK:
23
+ return 1000 * 60 * 60 * 24 * 7;
24
+ case Precision.MONTH:
25
+ return 1000 * 60 * 60 * 24 * 30;
26
+ case Precision.YEAR:
27
+ return 1000 * 60 * 60 * 24 * 365;
28
+ default:
29
+ throw new Error('Unsupported precision');
30
+ }
31
+ }
@@ -0,0 +1,19 @@
1
+ import { Interval } from './interval';
2
+ import { Precision } from './precision';
3
+ export declare function getDatesWithInterval(start: Date, end: Date, interval: Interval): Date[];
4
+ export declare function getWeeksWithInterval(start: Date, end: Date, interval: Interval): Date[];
5
+ export declare function getDaysWithInterval(start: Date, end: Date, interval: Interval): Date[];
6
+ export declare function getHoursWithInterval(start: Date, end: Date, interval: Interval): Date[];
7
+ export declare function getMinutesWithInterval(start: Date, end: Date, interval: Interval): Date[];
8
+ export declare function getMonthsWithInterval(start: Date, end: Date, interval: Interval): Date[];
9
+ export declare function getYearsWithInterval(start: Date, end: Date, interval: Interval): Date[];
10
+ export declare function addToDate(date: Date, amount: number, unit: Precision): Date;
11
+ export declare function subtractFromDate(date: Date, amount: number, unit: Precision): Date;
12
+ export declare function isSameDay(date1: Date, date2: Date): boolean;
13
+ export declare function getWeekNumber(date: Date): number;
14
+ export declare function getQuarter(date: Date): number;
15
+ export declare function isLeapYear(year: number): boolean;
16
+ export declare function getDaysInMonth(year: number, month: number): number;
17
+ export declare function formatDate(date: Date, format: string): string;
18
+ export declare function parseDate(dateString: string, format: string): Date;
19
+ export declare function range(start: number, end: number, step?: number): number[];
package/dist/utils.js ADDED
@@ -0,0 +1,168 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.getDatesWithInterval = getDatesWithInterval;
4
+ exports.getWeeksWithInterval = getWeeksWithInterval;
5
+ exports.getDaysWithInterval = getDaysWithInterval;
6
+ exports.getHoursWithInterval = getHoursWithInterval;
7
+ exports.getMinutesWithInterval = getMinutesWithInterval;
8
+ exports.getMonthsWithInterval = getMonthsWithInterval;
9
+ exports.getYearsWithInterval = getYearsWithInterval;
10
+ exports.addToDate = addToDate;
11
+ exports.subtractFromDate = subtractFromDate;
12
+ exports.isSameDay = isSameDay;
13
+ exports.getWeekNumber = getWeekNumber;
14
+ exports.getQuarter = getQuarter;
15
+ exports.isLeapYear = isLeapYear;
16
+ exports.getDaysInMonth = getDaysInMonth;
17
+ exports.formatDate = formatDate;
18
+ exports.parseDate = parseDate;
19
+ exports.range = range;
20
+ const precision_1 = require("./precision");
21
+ function getDatesWithInterval(start, end, interval) {
22
+ const dates = [];
23
+ const current = new Date(start);
24
+ while (current <= end) {
25
+ dates.push(new Date(current));
26
+ current.setMinutes(current.getMinutes() + interval.getMinutesInterval());
27
+ }
28
+ return dates;
29
+ }
30
+ function getWeeksWithInterval(start, end, interval) {
31
+ const weeks = [];
32
+ let current = new Date(Date.UTC(start.getUTCFullYear(), start.getUTCMonth(), start.getUTCDate()));
33
+ // Move to the start of the week (Sunday)
34
+ current.setUTCDate(current.getUTCDate() - current.getUTCDay());
35
+ while (current <= end) {
36
+ weeks.push(new Date(current));
37
+ // Add the interval in minutes, converted to milliseconds
38
+ current = new Date(current.getTime() + interval.getMinutesInterval() * 60000);
39
+ // Ensure we're at the start of the week
40
+ current.setUTCDate(current.getUTCDate() - current.getUTCDay());
41
+ }
42
+ return weeks;
43
+ }
44
+ function getDaysWithInterval(start, end, interval) {
45
+ const days = [];
46
+ const current = new Date(start);
47
+ current.setUTCHours(0, 0, 0, 0);
48
+ while (current <= end) {
49
+ days.push(new Date(current));
50
+ current.setUTCDate(current.getUTCDate() + interval.getMinutesInterval() / (24 * 60));
51
+ }
52
+ return days;
53
+ }
54
+ function getHoursWithInterval(start, end, interval) {
55
+ const hours = [];
56
+ const current = new Date(start);
57
+ current.setMinutes(0, 0, 0);
58
+ while (current <= end) {
59
+ hours.push(new Date(current));
60
+ current.setHours(current.getHours() + interval.getMinutesInterval() / 60);
61
+ }
62
+ return hours;
63
+ }
64
+ function getMinutesWithInterval(start, end, interval) {
65
+ const minutes = [];
66
+ const current = new Date(start);
67
+ current.setSeconds(0, 0);
68
+ while (current <= end) {
69
+ minutes.push(new Date(current));
70
+ current.setMinutes(current.getMinutes() + interval.getMinutesInterval());
71
+ }
72
+ return minutes;
73
+ }
74
+ function getMonthsWithInterval(start, end, interval) {
75
+ const months = [];
76
+ const current = new Date(Date.UTC(start.getUTCFullYear(), start.getUTCMonth(), 1));
77
+ while (current <= end) {
78
+ months.push(new Date(current));
79
+ current.setUTCMonth(current.getUTCMonth() + interval.getMinutesInterval() / (30 * 24 * 60));
80
+ }
81
+ return months;
82
+ }
83
+ function getYearsWithInterval(start, end, interval) {
84
+ const years = [];
85
+ const current = new Date(Date.UTC(start.getUTCFullYear(), 0, 1));
86
+ while (current <= end) {
87
+ years.push(new Date(current));
88
+ // Calculate the number of years to add based on the interval
89
+ const yearsToAdd = Math.max(1, Math.round(interval.getMinutesInterval() / 525600)); // 525600 minutes in a year
90
+ current.setUTCFullYear(current.getUTCFullYear() + yearsToAdd);
91
+ }
92
+ return years;
93
+ }
94
+ function addToDate(date, amount, unit) {
95
+ const newDate = new Date(Date.UTC(date.getUTCFullYear(), date.getUTCMonth(), date.getUTCDate(), date.getUTCHours(), date.getUTCMinutes(), date.getUTCSeconds(), date.getUTCMilliseconds()));
96
+ switch (unit) {
97
+ case precision_1.Precision.MINUTE:
98
+ newDate.setUTCMinutes(newDate.getUTCMinutes() + amount);
99
+ break;
100
+ case precision_1.Precision.HOUR:
101
+ newDate.setUTCHours(newDate.getUTCHours() + amount);
102
+ break;
103
+ case precision_1.Precision.DAY:
104
+ newDate.setUTCDate(newDate.getUTCDate() + amount);
105
+ break;
106
+ case precision_1.Precision.WEEK:
107
+ newDate.setUTCDate(newDate.getUTCDate() + amount * 7);
108
+ break;
109
+ case precision_1.Precision.MONTH:
110
+ newDate.setUTCMonth(newDate.getUTCMonth() + amount);
111
+ if (newDate.getUTCDate() !== date.getUTCDate()) {
112
+ newDate.setUTCDate(0); // Set to last day of previous month
113
+ }
114
+ break;
115
+ case precision_1.Precision.YEAR:
116
+ newDate.setUTCFullYear(newDate.getUTCFullYear() + amount);
117
+ break;
118
+ }
119
+ return newDate;
120
+ }
121
+ function subtractFromDate(date, amount, unit) {
122
+ return addToDate(date, -amount, unit);
123
+ }
124
+ function isSameDay(date1, date2) {
125
+ return (date1.getFullYear() === date2.getFullYear() &&
126
+ date1.getMonth() === date2.getMonth() &&
127
+ date1.getDate() === date2.getDate());
128
+ }
129
+ function getWeekNumber(date) {
130
+ const d = new Date(Date.UTC(date.getFullYear(), date.getMonth(), date.getDate()));
131
+ const dayNum = d.getUTCDay() || 7;
132
+ d.setUTCDate(d.getUTCDate() + 4 - dayNum);
133
+ const yearStart = new Date(Date.UTC(d.getUTCFullYear(), 0, 1));
134
+ return Math.ceil(((d.getTime() - yearStart.getTime()) / 86400000 + 1) / 7);
135
+ }
136
+ function getQuarter(date) {
137
+ return Math.floor(date.getMonth() / 3) + 1;
138
+ }
139
+ function isLeapYear(year) {
140
+ return (year % 4 === 0 && year % 100 !== 0) || year % 400 === 0;
141
+ }
142
+ function getDaysInMonth(year, month) {
143
+ return new Date(year, month + 1, 0).getDate();
144
+ }
145
+ function formatDate(date, format) {
146
+ const pad = (n) => n.toString().padStart(2, '0');
147
+ const map = {
148
+ YYYY: date.getFullYear().toString(),
149
+ MM: pad(date.getMonth() + 1),
150
+ DD: pad(date.getDate()),
151
+ HH: pad(date.getHours()),
152
+ mm: pad(date.getMinutes()),
153
+ ss: pad(date.getSeconds()),
154
+ };
155
+ return format.replace(/YYYY|MM|DD|HH|mm|ss/gi, (matched) => map[matched]);
156
+ }
157
+ function parseDate(dateString, format) {
158
+ const map = {};
159
+ const formatParts = format.match(/YYYY|MM|DD|HH|mm|ss/gi) || [];
160
+ const dateParts = dateString.match(/\d+/g) || [];
161
+ formatParts.forEach((part, index) => {
162
+ map[part] = parseInt(dateParts[index]);
163
+ });
164
+ return new Date(map['YYYY'] || 0, (map['MM'] || 1) - 1, map['DD'] || 1, map['HH'] || 0, map['mm'] || 0, map['ss'] || 0);
165
+ }
166
+ function range(start, end, step = 1) {
167
+ return Array.from({ length: Math.floor((end - start) / step) + 1 }, (_, i) => start + i * step);
168
+ }
package/package.json ADDED
@@ -0,0 +1,68 @@
1
+ {
2
+ "name": "chronos-ts",
3
+ "version": "1.0.0",
4
+ "description": "A comprehensive TypeScript package for handling time periods, intervals, and date-related operations.",
5
+ "main": "dist/index.js",
6
+ "types": "dist/index.d.ts",
7
+ "scripts": {
8
+ "build": "tsc",
9
+ "test": "jest",
10
+ "format": "prettier --write 'src/**/*.{ts,js,json,md}'",
11
+ "lint": "eslint 'src/**/*.{ts,js}'",
12
+ "prepare": "npm run build",
13
+ "prepublishOnly": "npm run format && npm run lint && npm test",
14
+ "postpublish": "git push --follow-tags"
15
+ },
16
+ "keywords": [
17
+ "time",
18
+ "date",
19
+ "period",
20
+ "interval",
21
+ "duration",
22
+ "range",
23
+ "typescript",
24
+ "calendar",
25
+ "datetime",
26
+ "time-management",
27
+ "date-range",
28
+ "date-manipulation",
29
+ "time-utils",
30
+ "date-utils",
31
+ "temporal",
32
+ "scheduling",
33
+ "time-period",
34
+ "date-interval",
35
+ "time-arithmetic",
36
+ "date-arithmetic"
37
+ ],
38
+ "files": [
39
+ "dist/**",
40
+ "README.md"
41
+ ],
42
+ "repository": {
43
+ "type": "git",
44
+ "url": "https://github.com/hendurhance/chronos-ts.git"
45
+ },
46
+ "author": "Josiah Endurance",
47
+ "homepage": "https://github.com/hendurhance/chronos-ts#readme",
48
+ "contributors": [
49
+ {
50
+ "name": "Josiah Endurance",
51
+ "email": "hendurhance.dev@gmail.com"
52
+ }
53
+ ],
54
+ "license": "MIT",
55
+ "devDependencies": {
56
+ "@eslint/js": "^9.10.0",
57
+ "@types/eslint__js": "^8.42.3",
58
+ "@types/jest": "^29.5.13",
59
+ "@types/node": "^22.5.5",
60
+ "eslint": "^9.10.0",
61
+ "globals": "^15.9.0",
62
+ "jest": "^29.7.0",
63
+ "prettier": "^3.3.3",
64
+ "ts-jest": "^29.2.5",
65
+ "typescript": "^5.6.2",
66
+ "typescript-eslint": "^8.6.0"
67
+ }
68
+ }