chronos-ts 1.1.0 → 2.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/README.md CHANGED
@@ -1,39 +1,30 @@
1
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
- - [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)
14
- - [Classes](#classes)
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)
23
- - [Utility Function](#utility-function)
24
- - [Real-world Scenarios](#real-world-scenarios)
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-)
35
-
36
- ## Installation
2
+
3
+ [![npm version](https://img.shields.io/npm/v/chronos-ts.svg)](https://www.npmjs.com/package/chronos-ts)
4
+ [![npm downloads](https://img.shields.io/npm/dt/chronos-ts.svg)](https://www.npmjs.com/package/chronos-ts)
5
+ [![tests](https://img.shields.io/badge/tests-passing-brightgreen.svg)](https://github.com/hendurhance/chronos-ts/actions)
6
+ [![build](https://img.shields.io/badge/build-passing-brightgreen.svg)](https://github.com/hendurhance/chronos-ts/actions)
7
+ [![coverage](https://img.shields.io/badge/coverage-unknown-lightgrey.svg)](https://github.com/hendurhance/chronos-ts/actions)
8
+
9
+ **Chronos-ts** — named after the Greek god of time — is a comprehensive TypeScript library for date and time manipulation. Version 2.0 is a complete rewrite inspired by [Carbon PHP](https://carbon.nesbot.com/), bringing modern, intuitive date handling to TypeScript and JavaScript.
10
+
11
+ > [!WARNING]
12
+ > This is a major rewrite (v2.0) and is not backward compatible with v1.x. Please refer to the [migration guide](MIGRATION.md) for details.
13
+
14
+ ## ✨ Features
15
+
16
+ - 🎯 **Intuitive API** — Fluent, chainable methods for all date operations
17
+ - 📅 **Immutable by Default** — All operations return new instances
18
+ - 🌍 **Timezone Support** — Built-in timezone handling with DST awareness
19
+ - 🌐 **Internationalization** — Extensible locale system with human-readable output
20
+ - ⏱️ **Intervals** — Powerful duration/interval handling (like CarbonInterval)
21
+ - 📆 **Periods** — Date range iteration with filtering and transformations
22
+ - 📋 **Period Collections** — Manage and analyze multiple date periods easily
23
+ - 📐 **Type-Safe** — Full TypeScript support with comprehensive types
24
+ - 🪶 **Zero Dependencies** — No external runtime dependencies
25
+
26
+ ## 📦 Installation
27
+
37
28
  ```bash
38
29
  npm install chronos-ts
39
30
 
@@ -41,485 +32,300 @@ npm install chronos-ts
41
32
  yarn add chronos-ts
42
33
 
43
34
  # or
44
-
45
35
  pnpm add chronos-ts
46
36
  ```
47
37
 
48
- ## Overview
49
- 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:
50
- - Flexible period creation and manipulation with customizable precision (minute to year)
51
- - Interval generation for recurring events (e.g., "every 3 days")
52
- - Comprehensive period comparisons (overlaps, contains, adjacent)
53
- - Precise start/end date handling with various time units
54
- - Advanced operations like symmetric differences and unions between periods
55
- - Adjacent period detection for seamless time range management
56
- - Extensive utility functions for common date operations and formatting
57
-
58
- 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.
59
-
38
+ ## 🚀 Quick Start
60
39
 
61
- ## Usage
62
- ### Creating and Manipulating Periods
63
40
  ```typescript
64
- import { Period, Precision, Interval } from 'chronos-ts';
65
-
66
- // Create a period for the year 2023
67
- const year2023 = new Period('2023-01-01', '2023-12-31', Precision.DAY);
68
-
69
- // Check if a date is within the period
70
- const isInPeriod = year2023.contains('2023-06-15'); // true
71
-
72
- // Create a period for Q2 2023
73
- const q2_2023 = new Period('2023-04-01', '2023-06-30', Precision.DAY);
41
+ import { Chronos, ChronosInterval, ChronosPeriod } from 'chronos-ts';
42
+
43
+ // Create dates
44
+ const now = Chronos.now();
45
+ const birthday = Chronos.create(1990, 6, 15);
46
+ const parsed = Chronos.parse('2024-03-15T10:30:00');
47
+
48
+ // Manipulate dates
49
+ const nextWeek = now.addWeeks(1);
50
+ const lastMonth = now.subtractMonths(1);
51
+ const startOfDay = now.startOf('day');
52
+
53
+ // Format dates
54
+ console.log(now.format('YYYY-MM-DD HH:mm:ss')); // "2024-03-15 14:30:45"
55
+ console.log(now.diffForHumans(birthday)); // "33 years ago"
56
+
57
+ // Work with intervals
58
+ const interval = ChronosInterval.create({ hours: 2, minutes: 30 });
59
+ console.log(interval.forHumans()); // "2 hours 30 minutes"
60
+
61
+ // Iterate over periods
62
+ const thisMonth = ChronosPeriod.thisMonth();
63
+ for (const day of thisMonth) {
64
+ console.log(day.format('YYYY-MM-DD'));
65
+ }
66
+ ```
74
67
 
75
- // Check if periods overlap
76
- const periodsOverlap = year2023.overlapsWith(q2_2023); // true
68
+ ## 📖 API Reference
77
69
 
78
- // Get the overlapping period
79
- const overlap = year2023.overlap(q2_2023);
80
- console.log(overlap?.getStartDate(), overlap?.getEndDate()); // 2023-04-01, 2023-06-30
70
+ For detailed documentation on all classes and methods, please refer to the [API Reference](API_REFERENCE.md).
81
71
 
82
- // Subtract a period
83
- const remainingPeriods = year2023.subtract(q2_2023);
84
- console.log(remainingPeriods.length); // 2
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
72
+ ### Core Classes
87
73
 
88
- // Create a period with an interval
89
- const weeklyPeriod = new Period('2023-01-01', '2023-12-31', Precision.WEEK, Interval.weeks(1));
74
+ - **[Chronos](API_REFERENCE.md#chronos)**: The main class for date/time manipulation.
75
+ - **[ChronosInterval](API_REFERENCE.md#chronosinterval)**: Represents a duration of time.
76
+ - **[ChronosPeriod](API_REFERENCE.md#chronosperiod)**: Represents a date range or schedule.
77
+ - **[ChronosPeriodCollection](API_REFERENCE.md#chronosperiodcollection)**: Manages collections of periods.
78
+ - **[ChronosTimezone](API_REFERENCE.md#chronostimezone)**: Timezone utilities.
90
79
 
91
- // Get dates in the interval
92
- const weeklyDates = weeklyPeriod.getDatesInInterval();
93
- console.log(weeklyDates?.length); // 53 (number of weeks in 2023)
80
+ ---
94
81
 
95
- // Renew a period - FIXED: Use getter methods
96
- const nextYear = year2023.renew();
97
- console.log(nextYear.getStartDate(), nextYear.getEndDate()); // 2024-01-01, 2024-12-31
82
+ ## 🌍 Localization
98
83
 
99
- // Use fluent API
100
- const customPeriod = new Period('2023-01-01', '2023-12-31')
101
- .setPrecision(Precision.MONTH)
102
- .setInterval(Interval.months(3));
84
+ Chronos-ts includes built-in support for English and Spanish, with an extensible locale system.
103
85
 
104
- console.log(customPeriod.getDatesInInterval()?.length); // 5 (Jan, Apr, Jul, Oct, Jan)
86
+ ```typescript
87
+ import { Chronos, registerLocale, getLocale } from 'chronos-ts';
88
+
89
+ // Use built-in locale
90
+ const date = Chronos.now().locale('es');
91
+ date.format('dddd, D [de] MMMM [de] YYYY');
92
+ // "viernes, 15 de marzo de 2024"
93
+
94
+ // Human-readable in Spanish
95
+ date.diffForHumans(Chronos.yesterday());
96
+ // "hace 1 día"
97
+
98
+ // Register custom locale
99
+ registerLocale({
100
+ code: 'de',
101
+ months: ['Januar', 'Februar', 'März', 'April', 'Mai', 'Juni',
102
+ 'Juli', 'August', 'September', 'Oktober', 'November', 'Dezember'],
103
+ monthsShort: ['Jan', 'Feb', 'Mär', 'Apr', 'Mai', 'Jun',
104
+ 'Jul', 'Aug', 'Sep', 'Okt', 'Nov', 'Dez'],
105
+ weekdays: ['Sonntag', 'Montag', 'Dienstag', 'Mittwoch',
106
+ 'Donnerstag', 'Freitag', 'Samstag'],
107
+ weekdaysShort: ['So', 'Mo', 'Di', 'Mi', 'Do', 'Fr', 'Sa'],
108
+ weekdaysMin: ['So', 'Mo', 'Di', 'Mi', 'Do', 'Fr', 'Sa'],
109
+ relativeTime: {
110
+ future: 'in %s',
111
+ past: 'vor %s',
112
+ s: 'wenigen Sekunden',
113
+ ss: '%d Sekunden',
114
+ m: 'einer Minute',
115
+ mm: '%d Minuten',
116
+ h: 'einer Stunde',
117
+ hh: '%d Stunden',
118
+ d: 'einem Tag',
119
+ dd: '%d Tagen',
120
+ M: 'einem Monat',
121
+ MM: '%d Monaten',
122
+ y: 'einem Jahr',
123
+ yy: '%d Jahren',
124
+ },
125
+ });
105
126
  ```
106
127
 
107
- ### Working with Intervals
108
- ``` typescript
109
- import { Interval } from 'chronos-ts';
110
-
111
- const twoHours = Interval.hours(2);
112
- console.log(twoHours.getMinutesInterval()); // 120
128
+ ---
113
129
 
114
- const threeWeeks = Interval.weeks(3);
115
- console.log(threeWeeks.getMinutesInterval()); // 30240 (3 * 7 * 24 * 60)
130
+ ## 🔧 Configuration
116
131
 
117
- const customInterval = new Interval(30, 2, 1, 0, 1); // 30 minutes, 2 hours, 1 day, 0 weeks, 1 month
118
- console.log(customInterval.getMinutesInterval()); // 44310 (30 + 120 + 1440 + 43200)
119
- ```
120
- ### Using Utility Functions
121
132
  ```typescript
122
- import { addToDate, subtractFromDate, formatDate, parseDate, getWeekNumber, isLeapYear, Precision } from 'chronos-ts';
123
-
124
- const today = new Date();
125
-
126
- // Add 3 months to today
127
- const futureDate = addToDate(today, 3, Precision.MONTH);
133
+ import { Chronos } from 'chronos-ts';
128
134
 
129
- // Subtract 2 weeks from today
130
- const pastDate = subtractFromDate(today, 2, Precision.WEEK);
135
+ // Set global configuration
136
+ Chronos.configure({
137
+ defaultTimezone: 'America/New_York',
138
+ defaultLocale: 'en',
139
+ });
131
140
 
132
- // Format a date
133
- const formattedDate = formatDate(today, 'YYYY-MM-DD HH:mm:ss');
141
+ // Set test/mock time (useful for testing)
142
+ Chronos.setTestNow(Chronos.create(2024, 1, 1));
143
+ const now = Chronos.now(); // Returns 2024-01-01
134
144
 
135
- // Parse a date string
136
- const parsedDate = parseDate('2023-06-15 14:30:00', 'YYYY-MM-DD HH:mm:ss');
137
-
138
- // Get the week number
139
- const weekNumber = getWeekNumber(today);
140
-
141
- // Check if it's a leap year
142
- const isLeap = isLeapYear(2024); // true
145
+ // Reset test time
146
+ Chronos.setTestNow(null);
143
147
  ```
144
148
 
149
+ ---
145
150
 
151
+ ## 📚 Real-World Examples
146
152
 
147
- ## API Reference
148
- ### Classes
149
- #### Period
150
- The `Period` class represents a time period with a start date, end date, precision, and optional interval.
151
-
152
- ##### Constructor
153
- ```typescript
154
- constructor(start: string | Date, end: string | Date, precision: Precision = Precision.DAY, interval: Interval | null = null)
155
- ```
153
+ ### Event Planning
156
154
 
157
- ##### Methods
158
- - `contains(date: string | Date): boolean`: Checks if the given date is within the period.
159
- - `overlapsWith(other: Period): boolean`: Checks if this period overlaps with another period.
160
- - `isAdjacentTo(other: Period): boolean`: Checks if this period is adjacent to another period.
161
- - `getDatesInInterval(): Date[] | null`: Returns an array of dates within the period based on the interval.
162
- - `getMinutesInInterval(): number`: Returns the number of minutes in the period.
163
- - `getHoursInInterval(): number`: Returns the number of hours in the period.
164
- - `getDaysInInterval(): number`: Returns the number of days in the period.
165
- - `getWeeksInInterval(): number`: Returns the number of weeks in the period.
166
- - `getMonthsInInterval(): number`: Returns the number of months in the period.
167
- - `getYearsInInterval(): number`: Returns the number of years in the period.
168
- - `length(): number`: Returns the length of the period in the specified precision.
169
- - `overlap(other: Period): Period | null`: Returns the overlapping period with another period, if any.
170
- - `subtract(other: Period): Period[]`: Subtracts another period from this period.
171
- - `gap(other: Period): Period | null`: Returns the gap between this period and another period, if any.
172
- - `symmetricDifference(other: Period): Period[]`: Returns the symmetric difference between this period and another period.
173
- - `renew(): Period`: Creates a new period of the same length immediately following this period.
174
- - `union(other: Period): Period[]`: Returns the union of this period with another period.
175
-
176
- ##### Fluent API Methods
177
-
178
- `setStart(start: string | Date): this`: Sets the start date of the period.
179
- `setEnd(end: string | Date): this`: Sets the end date of the period.
180
- `setPrecision(precision: Precision): this`: Sets the precision of the period.
181
- `setInterval(interval: Interval): this`: Sets the interval of the period.
182
-
183
- #### Interval
184
- The `Interval` class represents a time interval with minutes, hours, days, weeks, and months.
185
- Static Methods
186
-
187
- - `minutes(minutes: number): Interval:` Creates an interval with the specified number of minutes.
188
- - `hours(hours: number): Interval`: Creates an interval with the specified number of hours.
189
- - `days(days: number): Interval`: Creates an interval with the specified number of days.
190
- - `weeks(weeks: number): Interval`: Creates an interval with the specified number of weeks.
191
- - `months(months: number): Interval`: Creates an interval with the specified number of months.
192
-
193
- ##### Methods
194
-
195
- `getMinutesInterval(): number`: Returns the total number of minutes in the interval.
196
-
197
-
198
- #### Enums
199
- ##### Precision
200
- The Precision enum represents different levels of time precision.
201
155
  ```typescript
202
- enum Precision {
203
- MINUTE = 'minute',
204
- HOUR = 'hour',
205
- DAY = 'day',
206
- WEEK = 'week',
207
- MONTH = 'month',
208
- YEAR = 'year',
209
- }
210
- ```
211
- ### Utility Function
212
- The package includes various utility functions for working with dates:
213
-
214
- - `getDatesWithInterval(start: Date, end: Date, interval: Interval): Date[]` - Returns an array of dates within the specified interval.
215
- - `getWeeksWithInterval(start: Date, end: Date, interval: Interval): Date[]` - Returns an array of weeks within the specified interval.
216
- - `getDaysWithInterval(start: Date, end: Date, interval: Interval): Date[]` - Returns an array of days within the specified interval.
217
- - `getHoursWithInterval(start: Date, end: Date, interval: Interval): Date[]` - Returns an array of hours within the specified interval.
218
- - `getMinutesWithInterval(start: Date, end: Date, interval: Interval): Date[]` - Returns an array of minutes within the specified interval.
219
- - `getMonthsWithInterval(start: Date, end: Date, interval: Interval): Date[]` - Returns an array of months within the specified interval.
220
- - `getYearsWithInterval(start: Date, end: Date, interval: Interval): Date[]` - Returns an array of years within the specified interval.
221
- - `addToDate(date: Date, amount: number, unit: Precision): Date` - Adds the specified amount of time to a date.
222
- - `subtractFromDate(date: Date, amount: number, unit: Precision): Date` - Subtracts the specified amount of time from a date.
223
- - `isSameDay(date1: Date, date2: Date): boolean` - Checks if two dates are on the same day.
224
- - `getWeekNumber(date: Date): number` - Returns the week number of a date.
225
- - `getQuarter(date: Date): number` - Returns the quarter of a date.
226
- - `isLeapYear(year: number): boolean` - Checks if a year is a leap year.
227
- - `getDaysInMonth(year: number, month: number): number` - Returns the number of days in a month.
228
- - `formatDate(date: Date, format: string): string`
229
- - `parseDate(dateString: string, format: string): Date` - Parses a date string using the specified format.
230
- - `range(start: number, end: number, step: number = 1): number[]` - Returns an array of numbers within the specified range.
231
-
232
- ### Real-world Scenarios
233
- #### **1. Event Planning and Management**
234
- The `Period` class can be used to represent events, while the `Interval` class can help manage recurring events.
235
- ``` typescript
236
- import { Period, Interval, Precision } from 'chronos-ts';
237
-
238
- // Create an event
239
- const conference = new Period('2023-09-15 09:00', '2023-09-17 18:00', Precision.HOUR);
240
-
241
- // Check if a specific time is during the conference
242
- const isDuringConference = conference.contains('2023-09-16 14:30'); // true
243
-
244
- // Create a recurring weekly meeting
245
- const weeklyMeeting = new Period('2023-01-01 10:00', '2023-12-31 11:00', Precision.HOUR, Interval.weeks(1));
246
-
247
- // Get all meeting dates
248
- const meetingDates = weeklyMeeting.getDatesInInterval();
249
-
250
- // Check for conflicts with the conference
251
- const conflictingMeetings = meetingDates?.filter(date => conference.contains(date)) || [];
252
- console.log(`There are ${conflictingMeetings.length} conflicting meetings during the conference.`);
253
- ```
254
-
255
- #### **2. Financial Reporting Periods**
256
- Use the `Period` class to represent financial quarters and calculate year-to-date periods.
257
- ``` typescript
258
- import { Period, Precision } from 'chronos-ts';
259
-
260
- const q1_2023 = new Period('2023-01-01', '2023-03-31', Precision.DAY);
261
- const q2_2023 = new Period('2023-04-01', '2023-06-30', Precision.DAY);
262
- const q3_2023 = new Period('2023-07-01', '2023-09-30', Precision.DAY);
263
- const q4_2023 = new Period('2023-10-01', '2023-12-31', Precision.DAY);
156
+ import { Chronos, ChronosPeriod, ChronosInterval } from 'chronos-ts';
264
157
 
265
- // Calculate Year-to-Date period - FIXED: Use getter method
266
- const ytd = (currentQuarter: Period): Period => {
267
- return new Period('2023-01-01', currentQuarter.getEndDate(), Precision.DAY);
158
+ // Conference dates
159
+ const conference = {
160
+ start: Chronos.create(2024, 6, 15, 9, 0),
161
+ end: Chronos.create(2024, 6, 17, 18, 0),
268
162
  };
269
163
 
270
- const q3YTD = ytd(q3_2023);
271
- console.log(`Q3 YTD period: ${q3YTD.getStartDate().toDateString()} - ${q3YTD.getEndDate().toDateString()}`);
272
-
273
- // Calculate quarter-over-quarter growth
274
- const calculateQoQGrowth = (currentQuarter: number, previousQuarter: number): string => {
275
- const growth = (currentQuarter - previousQuarter) / previousQuarter * 100;
276
- return `${growth.toFixed(2)}%`;
277
- };
278
-
279
- const q2Revenue = 1000000;
280
- const q3Revenue = 1200000;
281
- console.log(`Q3 QoQ Growth: ${calculateQoQGrowth(q3Revenue, q2Revenue)}`);
164
+ // Check if a session conflicts
165
+ const session = Chronos.create(2024, 6, 16, 14, 0);
166
+ const isDuringConference = session.isBetween(conference.start, conference.end);
167
+
168
+ // Create weekly recurring meeting
169
+ const meetings = ChronosPeriod
170
+ .create(
171
+ Chronos.now(),
172
+ Chronos.now().addMonths(3),
173
+ { weeks: 1 }
174
+ )
175
+ .filter(date => date.dayOfWeek === 1) // Mondays only
176
+ .toArray();
282
177
  ```
283
178
 
284
- #### **3. Employee Leave Management**
285
- Use the `Period` class to manage employee leave requests and calculate leave balances.
286
- ``` typescript
287
- import { Period, Precision } from 'chronos-ts';
288
-
289
- // Create a leave request period
290
- const leaveRequest = new Period('2023-09-15', '2023-09-17', Precision.DAY);
291
-
292
- // Check if the leave request overlaps with existing leave periods
293
- const existingLeaves = [
294
- new Period('2023-09-10', '2023-09-14', Precision.DAY),
295
- new Period('2023-09-18', '2023-09-20', Precision.DAY),
296
- ];
179
+ ### Subscription Management
297
180
 
298
- const overlappingLeaves = existingLeaves.filter(leave => leaveRequest.overlapsWith(leave));
299
- console.log(`There are ${overlappingLeaves.length} overlapping leave requests.`);
300
-
301
- // Calculate remaining leave balance
302
- const totalLeaveDays = 20;
303
- const usedLeaveDays = existingLeaves.reduce((total, leave) => total + leave.length(), 0);
304
- const remainingLeaveDays = totalLeaveDays - usedLeaveDays;
305
- console.log(`Remaining leave days: ${remainingLeaveDays}`);
181
+ ```typescript
182
+ import { Chronos, ChronosInterval } from 'chronos-ts';
306
183
 
307
- // Renew leave balance for the next year
308
- const nextYearLeaveBalance = remainingLeaveDays > 0 ? new Period('2024-01-01', '2024-12-31') : null;
309
- console.log(`Next year's leave balance: ${nextYearLeaveBalance ? nextYearLeaveBalance.length() : 0} days`);
184
+ class Subscription {
185
+ constructor(
186
+ public startDate: Chronos,
187
+ public interval: ChronosInterval
188
+ ) {}
310
189
 
311
- ```
190
+ get nextBillingDate(): Chronos {
191
+ return this.startDate.add(this.interval);
192
+ }
312
193
 
313
- #### **4. Project Timeline Management**
314
- Use the Period class to manage project timelines and track overlapping tasks.
315
- ``` typescript
316
- import { Period, Precision } from 'chronos-ts';
317
-
318
- const projectTimeline = new Period('2023-01-01', '2023-12-31', Precision.DAY);
319
-
320
- const tasks = [
321
- new Period('2023-01-15', '2023-03-31', Precision.DAY),
322
- new Period('2023-03-01', '2023-05-15', Precision.DAY),
323
- new Period('2023-05-01', '2023-08-31', Precision.DAY),
324
- new Period('2023-08-15', '2023-11-30', Precision.DAY),
325
- ];
326
-
327
- // Find overlapping tasks
328
- const findOverlappingTasks = (tasks: Period[]): [Period, Period][] => {
329
- const overlaps: [Period, Period][] = [];
330
- for (let i = 0; i < tasks.length; i++) {
331
- for (let j = i + 1; j < tasks.length; j++) {
332
- if (tasks[i].overlapsWith(tasks[j])) {
333
- overlaps.push([tasks[i], tasks[j]]);
334
- }
335
- }
194
+ isActive(): boolean {
195
+ return Chronos.now().isBefore(this.nextBillingDate);
336
196
  }
337
- return overlaps;
338
- };
339
197
 
340
- const overlappingTasks = findOverlappingTasks(tasks);
341
- console.log(`There are ${overlappingTasks.length} overlapping tasks in the project.`);
198
+ daysUntilRenewal(): number {
199
+ return this.nextBillingDate.diffInDays(Chronos.now());
200
+ }
201
+ }
342
202
 
343
- // Calculate project progress
344
- const calculateProgress = (currentDate: Date): number => {
345
- const daysPassed = new Period(projectTimeline.getStartDate(), currentDate, Precision.DAY).getDaysInInterval();
346
- const totalDays = projectTimeline.getDaysInInterval();
347
- return (daysPassed / totalDays) * 100;
348
- };
203
+ const monthly = new Subscription(
204
+ Chronos.now(),
205
+ ChronosInterval.months(1)
206
+ );
349
207
 
350
- const currentProgress = calculateProgress(new Date('2023-06-15'));
351
- console.log(`Project progress: ${currentProgress.toFixed(2)}%`);
208
+ console.log(`Days until renewal: ${monthly.daysUntilRenewal()}`);
352
209
  ```
353
210
 
354
- #### **5. Subscription Billing Cycle Management**
355
- Use the Period class to manage subscription periods and calculate renewal dates.
356
- ``` typescript
357
- import { Period, Precision, addToDate } from 'chronos-ts';
211
+ ### Work Schedule
358
212
 
359
- class Subscription {
360
- constructor(public startDate: Date, public plan: 'monthly' | 'annual') {}
213
+ ```typescript
214
+ import { Chronos, ChronosPeriod } from 'chronos-ts';
361
215
 
362
- getCurrentPeriod(): Period {
363
- const endDate = addToDate(this.startDate, 1, this.plan === 'monthly' ? Precision.MONTH : Precision.YEAR);
364
- return new Period(this.startDate, endDate, Precision.DAY);
365
- }
216
+ // Get working days this month
217
+ const workingDays = ChronosPeriod
218
+ .thisMonth()
219
+ .filterWeekdays()
220
+ .toArray();
366
221
 
367
- isActive(date: Date = new Date()): boolean {
368
- return this.getCurrentPeriod().contains(date);
369
- }
222
+ console.log(`Working days this month: ${workingDays.length}`);
370
223
 
371
- getRenewalDate(): Date {
372
- return this.getCurrentPeriod().getEndDate();
373
- }
224
+ // Calculate hours worked
225
+ const clockIn = Chronos.create(2024, 3, 15, 9, 0);
226
+ const clockOut = Chronos.create(2024, 3, 15, 17, 30);
374
227
 
375
- renew(): void {
376
- this.startDate = this.getRenewalDate();
377
- }
378
- }
228
+ const hoursWorked = clockOut.diffInHours(clockIn);
229
+ const overtime = Math.max(0, hoursWorked - 8);
379
230
 
380
- const monthlySubscription = new Subscription(new Date('2023-01-01'), 'monthly');
381
- console.log(`Monthly subscription active: ${monthlySubscription.isActive()}`);
382
- console.log(`Monthly subscription renewal date: ${monthlySubscription.getRenewalDate().toDateString()}`);
231
+ console.log(`Hours worked: ${hoursWorked}`);
232
+ console.log(`Overtime: ${overtime}`);
233
+ ```
383
234
 
384
- const annualSubscription = new Subscription(new Date('2023-01-01'), 'annual');
385
- console.log(`Annual subscription active: ${annualSubscription.isActive()}`);
386
- console.log(`Annual subscription renewal date: ${annualSubscription.getRenewalDate().toDateString()}`);
235
+ ### Age Calculator
387
236
 
388
- // Check if a subscription will be active on a future date
389
- const futureDate = new Date('2023-12-15');
390
- console.log(`Monthly subscription active on ${futureDate.toDateString()}: ${monthlySubscription.isActive(futureDate)}`);
391
- console.log(`Annual subscription active on ${futureDate.toDateString()}: ${annualSubscription.isActive(futureDate)}`);
237
+ ```typescript
238
+ import { Chronos } from 'chronos-ts';
239
+
240
+ function getAge(birthDate: Chronos): { years: number; months: number; days: number } {
241
+ const now = Chronos.now();
242
+
243
+ return {
244
+ years: now.diffInYears(birthDate),
245
+ months: now.diffInMonths(birthDate) % 12,
246
+ days: now.diffInDays(birthDate.addYears(now.diffInYears(birthDate))) % 30,
247
+ };
248
+ }
392
249
 
393
- // Renew a subscription
394
- monthlySubscription.renew();
395
- const renewedPeriod = monthlySubscription.getCurrentPeriod();
396
- console.log(`Monthly subscription renewed. New period: ${renewedPeriod.getStartDate().toDateString()} - ${renewedPeriod.getEndDate().toDateString()}`);
250
+ const birthday = Chronos.create(1990, 6, 15);
251
+ const age = getAge(birthday);
252
+ console.log(`Age: ${age.years} years, ${age.months} months, ${age.days} days`);
397
253
  ```
398
254
 
399
- #### **6. Employee Shift Management**
255
+ ---
400
256
 
401
- Use the `Period` and `Interval` classes to manage employee shifts and calculate overtime.
257
+ ## 🧪 Testing
402
258
 
403
259
  ```typescript
404
- import { Period, Interval, Precision, addToDate } from 'chronos-ts';
260
+ import { Chronos } from 'chronos-ts';
261
+
262
+ describe('MyFeature', () => {
263
+ beforeEach(() => {
264
+ // Freeze time for consistent tests
265
+ Chronos.setTestNow(Chronos.create(2024, 1, 15, 12, 0, 0));
266
+ });
267
+
268
+ afterEach(() => {
269
+ // Reset to real time
270
+ Chronos.setTestNow(null);
271
+ });
272
+
273
+ it('should calculate correct deadline', () => {
274
+ const deadline = Chronos.now().addDays(30);
275
+ expect(deadline.format('YYYY-MM-DD')).toBe('2024-02-14');
276
+ });
277
+ });
278
+ ```
405
279
 
406
- class Shift extends Period {
407
- constructor(public employee: string, start: Date, end: Date) {
408
- super(start, end, Precision.MINUTE);
409
- }
280
+ ---
410
281
 
411
- getDuration(): number {
412
- return this.getHoursInInterval();
413
- }
282
+ ## 🆚 Comparison with Other Libraries
414
283
 
415
- isOvertime(): boolean {
416
- return this.getDuration() > 8;
417
- }
284
+ | Feature | Chronos-ts | Day.js | Moment.js | date-fns |
285
+ |---------|-----------|--------|-----------|----------|
286
+ | Immutable | ✅ | ✅ | ❌ | ✅ |
287
+ | TypeScript | ✅ Native | Plugin | Plugin | ✅ Native |
288
+ | Tree-shakeable | ✅ | ✅ | ❌ | ✅ |
289
+ | Intervals | ✅ | Plugin | ✅ | ❌ |
290
+ | Periods | ✅ | ❌ | ❌ | ❌ |
291
+ | Timezones | ✅ | Plugin | Plugin | ✅ |
292
+ | Zero deps | ✅ | ✅ | ❌ | ✅ |
293
+ | API Style | Fluent | Fluent | Fluent | Functional |
418
294
 
419
- getOvertimeHours(): number {
420
- return Math.max(0, this.getDuration() - 8);
421
- }
422
- }
295
+ ---
423
296
 
424
- // Create a week's worth of shifts for an employee
425
- const createWeekShifts = (employee: string, startDate: Date): Shift[] => {
426
- const shifts: Shift[] = [];
427
- for (let i = 0; i < 5; i++) { // Assuming 5-day work week
428
- const shiftStart = addToDate(startDate, i, Precision.DAY);
429
- shiftStart.setHours(9, 0, 0, 0); // 9 AM start
430
- const shiftEnd = new Date(shiftStart);
431
- shiftEnd.setHours(17, 0, 0, 0); // 5 PM end
432
- shifts.push(new Shift(employee, shiftStart, shiftEnd));
433
- }
434
- return shifts;
435
- };
297
+ ## 🤝 Contributing
436
298
 
437
- const employeeShifts = createWeekShifts('John Doe', new Date('2023-06-19'));
299
+ Contributions, issues, and feature requests are welcome! See [CONTRIBUTING.md](CONTRIBUTING.md) for guidelines.
438
300
 
439
- // Calculate total hours worked and overtime
440
- const totalHours = employeeShifts.reduce((sum, shift) => sum + shift.getDuration(), 0);
441
- const overtimeHours = employeeShifts.reduce((sum, shift) => sum + shift.getOvertimeHours(), 0);
301
+ ```bash
302
+ # Clone the repository
303
+ git clone https://github.com/hendurhance/chronos-ts.git
442
304
 
443
- console.log(`Total hours worked: ${totalHours}`);
444
- console.log(`Overtime hours: ${overtimeHours}`);
305
+ # Install dependencies
306
+ npm install
445
307
 
446
- // Check for conflicting shifts
447
- const hasConflict = (shifts: Shift[]): boolean => {
448
- for (let i = 0; i < shifts.length; i++) {
449
- for (let j = i + 1; j < shifts.length; j++) {
450
- if (shifts[i].overlapsWith(shifts[j])) {
451
- return true;
452
- }
453
- }
454
- }
455
- return false;
456
- };
308
+ # Run tests
309
+ npm test
457
310
 
458
- console.log(`Shifts have conflicts: ${hasConflict(employeeShifts)}`);
311
+ # Build
312
+ npm run build
459
313
  ```
460
314
 
461
- #### **7. Travel Itinerary Planning**
462
- Use the `Period` class to manage travel itineraries and check for scheduling conflicts.
463
- ``` typescript
464
- import { Period, Precision, addToDate } from 'chronos-ts';
315
+ ---
465
316
 
466
- class TravelEvent extends Period {
467
- constructor(public description: string, start: Date, end: Date) {
468
- super(start, end, Precision.MINUTE);
469
- }
470
- }
317
+ ## 📄 License
471
318
 
472
- class Itinerary {
473
- events: TravelEvent[] = [];
319
+ This project is licensed under the MIT License — see the [LICENSE](LICENSE) file for details.
474
320
 
475
- addEvent(event: TravelEvent): void {
476
- if (this.hasConflict(event)) {
477
- throw new Error('Event conflicts with existing events');
478
- }
479
- this.events.push(event);
480
- }
481
-
482
- hasConflict(newEvent: TravelEvent): boolean {
483
- return this.events.some(event => event.overlapsWith(newEvent));
484
- }
485
-
486
- getDuration(): number {
487
- if (this.events.length === 0) return 0;
488
- const start = this.events.reduce((min, e) => e.getStartDate() < min ? e.getStartDate() : min, this.events[0].getStartDate());
489
- const end = this.events.reduce((max, e) => e.getEndDate() > max ? e.getEndDate() : max, this.events[0].getEndDate());
490
- return new Period(start, end, Precision.HOUR).getDaysInInterval();
491
- }
492
- }
493
-
494
- // Create a travel itinerary
495
- const itinerary = new Itinerary();
496
-
497
- // Add events to the itinerary
498
- try {
499
- itinerary.addEvent(new TravelEvent('Flight to Paris', new Date('2023-07-01 10:00'), new Date('2023-07-01 12:00')));
500
- itinerary.addEvent(new TravelEvent('Hotel Check-in', new Date('2023-07-01 14:00'), new Date('2023-07-01 15:00')));
501
- itinerary.addEvent(new TravelEvent('Eiffel Tower Visit', new Date('2023-07-02 10:00'), new Date('2023-07-02 13:00')));
502
- itinerary.addEvent(new TravelEvent('Louvre Museum', new Date('2023-07-03 09:00'), new Date('2023-07-03 12:00')));
503
- itinerary.addEvent(new TravelEvent('Flight to Rome', new Date('2023-07-04 15:00'), new Date('2023-07-04 17:00')));
504
- } catch (error) {
505
- console.error('Error creating itinerary:', error.message);
506
- }
507
-
508
- console.log(`Itinerary duration: ${itinerary.getDuration()} days`);
509
-
510
- // Try to add a conflicting event
511
- try {
512
- itinerary.addEvent(new TravelEvent('Conflicting Event', new Date('2023-07-01 11:00'), new Date('2023-07-01 13:00')));
513
- } catch (error) {
514
- console.log('Caught conflicting event:', error.message);
515
- }
516
- ```
321
+ ---
517
322
 
518
- ## Contributing 🤝
519
- 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.
323
+ ## 🙏 Acknowledgements
520
324
 
521
- ## License 📝
522
- This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
325
+ - [Carbon PHP](https://carbon.nesbot.com/) — Primary API inspiration
326
+ - [Day.js](https://day.js.org/) Immutability patterns
327
+ - [Moment.js](https://momentjs.com/) — Format string patterns
328
+ - [Period](https://github.com/spatie/period) - Complex period comparisons
329
+ ---
523
330
 
524
- ## Acknowledgements 🙏
525
- - [spatie/period](https://github.com/spatie/period)
331
+ Made with ❤️ by the Chronos-ts team