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 +249 -443
- package/dist/core/chronos.d.ts +460 -0
- package/dist/core/chronos.js +1259 -0
- package/dist/core/index.d.ts +9 -0
- package/dist/core/index.js +19 -0
- package/dist/core/interval.d.ts +289 -0
- package/dist/core/interval.js +689 -0
- package/dist/core/period-collection.d.ts +205 -0
- package/dist/core/period-collection.js +562 -0
- package/dist/core/period.d.ts +428 -0
- package/dist/core/period.js +1007 -0
- package/dist/core/timezone.d.ts +289 -0
- package/dist/core/timezone.js +671 -0
- package/dist/index.d.ts +50 -4
- package/dist/index.js +148 -22
- package/dist/locales/index.d.ts +66 -0
- package/dist/locales/index.js +847 -0
- package/dist/types/index.d.ts +428 -0
- package/dist/types/index.js +71 -0
- package/dist/utils/index.d.ts +127 -0
- package/dist/utils/index.js +656 -0
- package/package.json +19 -3
- package/dist/interval.d.ts +0 -61
- package/dist/interval.js +0 -82
- package/dist/period.d.ts +0 -196
- package/dist/period.js +0 -365
- package/dist/precision.d.ts +0 -24
- package/dist/precision.js +0 -46
- package/dist/utils.d.ts +0 -190
- package/dist/utils.js +0 -374
package/README.md
CHANGED
|
@@ -1,39 +1,30 @@
|
|
|
1
1
|
# Chronos-ts ⏰
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
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
|
+
[](https://www.npmjs.com/package/chronos-ts)
|
|
4
|
+
[](https://www.npmjs.com/package/chronos-ts)
|
|
5
|
+
[](https://github.com/hendurhance/chronos-ts/actions)
|
|
6
|
+
[](https://github.com/hendurhance/chronos-ts/actions)
|
|
7
|
+
[](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
|
-
##
|
|
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 {
|
|
65
|
-
|
|
66
|
-
// Create
|
|
67
|
-
const
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
const
|
|
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
|
-
|
|
76
|
-
const periodsOverlap = year2023.overlapsWith(q2_2023); // true
|
|
68
|
+
## 📖 API Reference
|
|
77
69
|
|
|
78
|
-
|
|
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
|
-
|
|
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
|
-
|
|
89
|
-
|
|
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
|
-
|
|
92
|
-
const weeklyDates = weeklyPeriod.getDatesInInterval();
|
|
93
|
-
console.log(weeklyDates?.length); // 53 (number of weeks in 2023)
|
|
80
|
+
---
|
|
94
81
|
|
|
95
|
-
|
|
96
|
-
const nextYear = year2023.renew();
|
|
97
|
-
console.log(nextYear.getStartDate(), nextYear.getEndDate()); // 2024-01-01, 2024-12-31
|
|
82
|
+
## 🌍 Localization
|
|
98
83
|
|
|
99
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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 {
|
|
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
|
-
//
|
|
130
|
-
|
|
135
|
+
// Set global configuration
|
|
136
|
+
Chronos.configure({
|
|
137
|
+
defaultTimezone: 'America/New_York',
|
|
138
|
+
defaultLocale: 'en',
|
|
139
|
+
});
|
|
131
140
|
|
|
132
|
-
//
|
|
133
|
-
|
|
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
|
-
//
|
|
136
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
//
|
|
266
|
-
const
|
|
267
|
-
|
|
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
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
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
|
-
|
|
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
|
-
|
|
299
|
-
|
|
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
|
-
|
|
308
|
-
|
|
309
|
-
|
|
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
|
-
|
|
314
|
-
|
|
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
|
-
|
|
341
|
-
|
|
198
|
+
daysUntilRenewal(): number {
|
|
199
|
+
return this.nextBillingDate.diffInDays(Chronos.now());
|
|
200
|
+
}
|
|
201
|
+
}
|
|
342
202
|
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
return (daysPassed / totalDays) * 100;
|
|
348
|
-
};
|
|
203
|
+
const monthly = new Subscription(
|
|
204
|
+
Chronos.now(),
|
|
205
|
+
ChronosInterval.months(1)
|
|
206
|
+
);
|
|
349
207
|
|
|
350
|
-
|
|
351
|
-
console.log(`Project progress: ${currentProgress.toFixed(2)}%`);
|
|
208
|
+
console.log(`Days until renewal: ${monthly.daysUntilRenewal()}`);
|
|
352
209
|
```
|
|
353
210
|
|
|
354
|
-
|
|
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
|
-
|
|
360
|
-
|
|
213
|
+
```typescript
|
|
214
|
+
import { Chronos, ChronosPeriod } from 'chronos-ts';
|
|
361
215
|
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
216
|
+
// Get working days this month
|
|
217
|
+
const workingDays = ChronosPeriod
|
|
218
|
+
.thisMonth()
|
|
219
|
+
.filterWeekdays()
|
|
220
|
+
.toArray();
|
|
366
221
|
|
|
367
|
-
|
|
368
|
-
return this.getCurrentPeriod().contains(date);
|
|
369
|
-
}
|
|
222
|
+
console.log(`Working days this month: ${workingDays.length}`);
|
|
370
223
|
|
|
371
|
-
|
|
372
|
-
|
|
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
|
-
|
|
376
|
-
|
|
377
|
-
}
|
|
378
|
-
}
|
|
228
|
+
const hoursWorked = clockOut.diffInHours(clockIn);
|
|
229
|
+
const overtime = Math.max(0, hoursWorked - 8);
|
|
379
230
|
|
|
380
|
-
|
|
381
|
-
console.log(`
|
|
382
|
-
|
|
231
|
+
console.log(`Hours worked: ${hoursWorked}`);
|
|
232
|
+
console.log(`Overtime: ${overtime}`);
|
|
233
|
+
```
|
|
383
234
|
|
|
384
|
-
|
|
385
|
-
console.log(`Annual subscription active: ${annualSubscription.isActive()}`);
|
|
386
|
-
console.log(`Annual subscription renewal date: ${annualSubscription.getRenewalDate().toDateString()}`);
|
|
235
|
+
### Age Calculator
|
|
387
236
|
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
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
|
-
|
|
394
|
-
|
|
395
|
-
|
|
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
|
-
|
|
255
|
+
---
|
|
400
256
|
|
|
401
|
-
|
|
257
|
+
## 🧪 Testing
|
|
402
258
|
|
|
403
259
|
```typescript
|
|
404
|
-
import {
|
|
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
|
-
|
|
407
|
-
constructor(public employee: string, start: Date, end: Date) {
|
|
408
|
-
super(start, end, Precision.MINUTE);
|
|
409
|
-
}
|
|
280
|
+
---
|
|
410
281
|
|
|
411
|
-
|
|
412
|
-
return this.getHoursInInterval();
|
|
413
|
-
}
|
|
282
|
+
## 🆚 Comparison with Other Libraries
|
|
414
283
|
|
|
415
|
-
|
|
416
|
-
|
|
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
|
-
|
|
420
|
-
return Math.max(0, this.getDuration() - 8);
|
|
421
|
-
}
|
|
422
|
-
}
|
|
295
|
+
---
|
|
423
296
|
|
|
424
|
-
|
|
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
|
-
|
|
299
|
+
Contributions, issues, and feature requests are welcome! See [CONTRIBUTING.md](CONTRIBUTING.md) for guidelines.
|
|
438
300
|
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
301
|
+
```bash
|
|
302
|
+
# Clone the repository
|
|
303
|
+
git clone https://github.com/hendurhance/chronos-ts.git
|
|
442
304
|
|
|
443
|
-
|
|
444
|
-
|
|
305
|
+
# Install dependencies
|
|
306
|
+
npm install
|
|
445
307
|
|
|
446
|
-
|
|
447
|
-
|
|
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
|
-
|
|
311
|
+
# Build
|
|
312
|
+
npm run build
|
|
459
313
|
```
|
|
460
314
|
|
|
461
|
-
|
|
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
|
-
|
|
467
|
-
constructor(public description: string, start: Date, end: Date) {
|
|
468
|
-
super(start, end, Precision.MINUTE);
|
|
469
|
-
}
|
|
470
|
-
}
|
|
317
|
+
## 📄 License
|
|
471
318
|
|
|
472
|
-
|
|
473
|
-
events: TravelEvent[] = [];
|
|
319
|
+
This project is licensed under the MIT License — see the [LICENSE](LICENSE) file for details.
|
|
474
320
|
|
|
475
|
-
|
|
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
|
-
##
|
|
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
|
-
|
|
522
|
-
|
|
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
|
-
|
|
525
|
-
- [spatie/period](https://github.com/spatie/period)
|
|
331
|
+
Made with ❤️ by the Chronos-ts team
|