ts-time-utils 0.0.1 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +590 -1
- package/dist/age.d.ts +1 -10
- package/dist/age.d.ts.map +1 -1
- package/dist/calculate.d.ts.map +1 -1
- package/dist/calculate.js +24 -10
- package/dist/constants.d.ts +2 -21
- package/dist/constants.d.ts.map +1 -1
- package/dist/constants.js +12 -13
- package/dist/countdown.d.ts +217 -0
- package/dist/countdown.d.ts.map +1 -0
- package/dist/countdown.js +298 -0
- package/dist/dateRange.d.ts +266 -0
- package/dist/dateRange.d.ts.map +1 -0
- package/dist/dateRange.js +433 -0
- package/dist/duration.d.ts +171 -0
- package/dist/duration.d.ts.map +1 -0
- package/dist/duration.js +382 -0
- package/dist/esm/age.d.ts +1 -10
- package/dist/esm/age.d.ts.map +1 -1
- package/dist/esm/calculate.d.ts.map +1 -1
- package/dist/esm/calculate.js +24 -10
- package/dist/esm/constants.d.ts +2 -21
- package/dist/esm/constants.d.ts.map +1 -1
- package/dist/esm/constants.js +12 -13
- package/dist/esm/countdown.d.ts +217 -0
- package/dist/esm/countdown.d.ts.map +1 -0
- package/dist/esm/countdown.js +298 -0
- package/dist/esm/dateRange.d.ts +266 -0
- package/dist/esm/dateRange.d.ts.map +1 -0
- package/dist/esm/dateRange.js +433 -0
- package/dist/esm/duration.d.ts +171 -0
- package/dist/esm/duration.d.ts.map +1 -0
- package/dist/esm/duration.js +382 -0
- package/dist/esm/format.d.ts.map +1 -1
- package/dist/esm/index.d.ts +14 -6
- package/dist/esm/index.d.ts.map +1 -1
- package/dist/esm/index.js +16 -0
- package/dist/esm/interval.d.ts +3 -6
- package/dist/esm/interval.d.ts.map +1 -1
- package/dist/esm/locale.d.ts +94 -0
- package/dist/esm/locale.d.ts.map +1 -0
- package/dist/esm/locale.js +1087 -0
- package/dist/esm/naturalLanguage.d.ts +107 -0
- package/dist/esm/naturalLanguage.d.ts.map +1 -0
- package/dist/esm/naturalLanguage.js +344 -0
- package/dist/esm/performance.d.ts +2 -9
- package/dist/esm/performance.d.ts.map +1 -1
- package/dist/esm/performance.js +7 -8
- package/dist/esm/rangePresets.d.ts +7 -8
- package/dist/esm/rangePresets.d.ts.map +1 -1
- package/dist/esm/rangePresets.js +11 -9
- package/dist/esm/recurrence.d.ts +149 -0
- package/dist/esm/recurrence.d.ts.map +1 -0
- package/dist/esm/recurrence.js +404 -0
- package/dist/esm/serialize.d.ts +73 -0
- package/dist/esm/serialize.d.ts.map +1 -0
- package/dist/esm/serialize.js +365 -0
- package/dist/esm/timezone.d.ts +2 -6
- package/dist/esm/timezone.d.ts.map +1 -1
- package/dist/esm/timezone.js +1 -1
- package/dist/esm/types.d.ts +250 -0
- package/dist/esm/types.d.ts.map +1 -0
- package/dist/esm/types.js +25 -0
- package/dist/esm/workingHours.d.ts +4 -13
- package/dist/esm/workingHours.d.ts.map +1 -1
- package/dist/esm/workingHours.js +3 -1
- package/dist/format.d.ts.map +1 -1
- package/dist/index.d.ts +14 -6
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +16 -0
- package/dist/interval.d.ts +3 -6
- package/dist/interval.d.ts.map +1 -1
- package/dist/locale.d.ts +94 -0
- package/dist/locale.d.ts.map +1 -0
- package/dist/locale.js +1087 -0
- package/dist/naturalLanguage.d.ts +107 -0
- package/dist/naturalLanguage.d.ts.map +1 -0
- package/dist/naturalLanguage.js +344 -0
- package/dist/performance.d.ts +2 -9
- package/dist/performance.d.ts.map +1 -1
- package/dist/performance.js +7 -8
- package/dist/rangePresets.d.ts +7 -8
- package/dist/rangePresets.d.ts.map +1 -1
- package/dist/rangePresets.js +11 -9
- package/dist/recurrence.d.ts +149 -0
- package/dist/recurrence.d.ts.map +1 -0
- package/dist/recurrence.js +404 -0
- package/dist/serialize.d.ts +73 -0
- package/dist/serialize.d.ts.map +1 -0
- package/dist/serialize.js +365 -0
- package/dist/timezone.d.ts +2 -6
- package/dist/timezone.d.ts.map +1 -1
- package/dist/timezone.js +1 -1
- package/dist/types.d.ts +250 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +25 -0
- package/dist/workingHours.d.ts +4 -13
- package/dist/workingHours.d.ts.map +1 -1
- package/dist/workingHours.js +3 -1
- package/package.json +67 -3
package/README.md
CHANGED
|
@@ -8,7 +8,61 @@ A lightweight TypeScript utility library for time formatting, calculations, and
|
|
|
8
8
|
- **⚡ Fast** - Zero dependencies, pure JavaScript functions
|
|
9
9
|
- **🔧 TypeScript** - Full type safety and IntelliSense support
|
|
10
10
|
- **🌳 Tree-shakable** - Import individual functions to minimize bundle size
|
|
11
|
-
- **📚 Comprehensive** -
|
|
11
|
+
- **📚 Comprehensive** - 19 utility categories with 150+ functions
|
|
12
|
+
|
|
13
|
+
### 🔄 Recurrence utilities **(NEW!)**
|
|
14
|
+
|
|
15
|
+
- RRULE-inspired recurring event patterns
|
|
16
|
+
- Daily, weekly, monthly, and yearly recurrences
|
|
17
|
+
- Complex recurrence rules with byWeekday, byMonthDay, byMonth
|
|
18
|
+
- Get next occurrence, all occurrences, or occurrences within range
|
|
19
|
+
- Human-readable recurrence descriptions
|
|
20
|
+
- Full support for count and until limits
|
|
21
|
+
|
|
22
|
+
### ⏲️ Countdown & Timer utilities **(NEW!)**
|
|
23
|
+
|
|
24
|
+
- Real-time countdown timers with callbacks
|
|
25
|
+
- Get remaining time broken down by units
|
|
26
|
+
- Format countdowns as human-readable strings
|
|
27
|
+
- Check if dates are expired
|
|
28
|
+
- Calculate progress percentage between dates
|
|
29
|
+
- Deadline tracking with helper methods
|
|
30
|
+
|
|
31
|
+
### 📊 Date Range utilities **(NEW!)**
|
|
32
|
+
|
|
33
|
+
- Advanced range operations (overlap, intersection, union)
|
|
34
|
+
- Merge overlapping ranges
|
|
35
|
+
- Find gaps between ranges
|
|
36
|
+
- Split ranges into chunks
|
|
37
|
+
- Expand and shrink ranges
|
|
38
|
+
- Subtract ranges from each other
|
|
39
|
+
- Check containment and sort ranges
|
|
40
|
+
|
|
41
|
+
### 💬 Natural Language Parsing **(NEW!)**
|
|
42
|
+
|
|
43
|
+
- Parse human-friendly date strings ("tomorrow", "next Friday", "in 2 weeks")
|
|
44
|
+
- Extract dates from text automatically
|
|
45
|
+
- Context-aware date suggestions ("end of month", "EOY")
|
|
46
|
+
- Support for relative phrases and absolute dates
|
|
47
|
+
- Confidence scoring for extracted dates
|
|
48
|
+
|
|
49
|
+
### ⏱️ Duration utilities
|
|
50
|
+
|
|
51
|
+
- Immutable Duration class with arithmetic operations
|
|
52
|
+
- Create durations from various units and string formats
|
|
53
|
+
- Add, subtract, multiply, divide durations
|
|
54
|
+
- Compare durations and check relationships
|
|
55
|
+
- Format to human-readable strings
|
|
56
|
+
- Utility functions for arrays of durations
|
|
57
|
+
|
|
58
|
+
### 💾 Serialization utilities
|
|
59
|
+
|
|
60
|
+
- Safe JSON date serialization and deserialization
|
|
61
|
+
- Multiple format support (ISO, epoch, object, custom)
|
|
62
|
+
- Automatic date reviver and replacer functions
|
|
63
|
+
- Timezone-aware serialization options
|
|
64
|
+
- Cross-platform date interchange utilities
|
|
65
|
+
- Validation for ISO strings and epoch timestamps
|
|
12
66
|
|
|
13
67
|
### 🎨 Format utilities
|
|
14
68
|
|
|
@@ -89,6 +143,16 @@ A lightweight TypeScript utility library for time formatting, calculations, and
|
|
|
89
143
|
- This/last/next week, month, quarter, year
|
|
90
144
|
- Rolling windows and quarter helpers
|
|
91
145
|
|
|
146
|
+
### 🌍 Locale utilities
|
|
147
|
+
|
|
148
|
+
- Multi-language relative time formatting
|
|
149
|
+
- Locale-specific date and time formatting
|
|
150
|
+
- Support for 40+ locales with built-in configurations
|
|
151
|
+
- Auto-detection of system/browser locale
|
|
152
|
+
- Custom locale registration
|
|
153
|
+
- Internationalization (i18n) support
|
|
154
|
+
- **Locale conversions** - Convert between different locales and detect locale from text
|
|
155
|
+
|
|
92
156
|
### 🧱 Constants
|
|
93
157
|
|
|
94
158
|
- Milliseconds & seconds per unit
|
|
@@ -122,10 +186,310 @@ import { createInterval, mergeIntervals } from "ts-time-utils/interval";
|
|
|
122
186
|
import { formatInTimeZone } from "ts-time-utils/timezone";
|
|
123
187
|
import { isWorkingTime, addWorkingHours } from "ts-time-utils/workingHours";
|
|
124
188
|
import { today, lastNDays } from "ts-time-utils/rangePresets";
|
|
189
|
+
import { Duration, createDuration } from "ts-time-utils/duration";
|
|
190
|
+
import { serializeDate, parseJSONWithDates } from "ts-time-utils/serialize";
|
|
191
|
+
import {
|
|
192
|
+
formatRelativeTime,
|
|
193
|
+
formatDateLocale,
|
|
194
|
+
detectLocale,
|
|
195
|
+
} from "ts-time-utils/locale";
|
|
196
|
+
// New modules!
|
|
197
|
+
import { createRecurrence, getNextOccurrence } from "ts-time-utils/recurrence";
|
|
198
|
+
import { createCountdown, getRemainingTime } from "ts-time-utils/countdown";
|
|
199
|
+
import { mergeDateRanges, findGaps } from "ts-time-utils/dateRange";
|
|
200
|
+
import {
|
|
201
|
+
parseNaturalDate,
|
|
202
|
+
extractDatesFromText,
|
|
203
|
+
} from "ts-time-utils/naturalLanguage";
|
|
125
204
|
```
|
|
126
205
|
|
|
127
206
|
## 📖 Examples
|
|
128
207
|
|
|
208
|
+
### Recurrence Utilities (NEW!)
|
|
209
|
+
|
|
210
|
+
```ts
|
|
211
|
+
import { createRecurrence, recurrenceToString } from "ts-time-utils/recurrence";
|
|
212
|
+
|
|
213
|
+
// Daily recurrence
|
|
214
|
+
const daily = createRecurrence({
|
|
215
|
+
frequency: "daily",
|
|
216
|
+
interval: 2,
|
|
217
|
+
startDate: new Date("2024-01-01"),
|
|
218
|
+
count: 10,
|
|
219
|
+
});
|
|
220
|
+
|
|
221
|
+
const next = daily.getNextOccurrence(new Date());
|
|
222
|
+
const allOccurrences = daily.getAllOccurrences();
|
|
223
|
+
|
|
224
|
+
// Weekly on specific days
|
|
225
|
+
const weekly = createRecurrence({
|
|
226
|
+
frequency: "weekly",
|
|
227
|
+
interval: 1,
|
|
228
|
+
startDate: new Date("2024-01-01"),
|
|
229
|
+
byWeekday: [1, 3, 5], // Monday, Wednesday, Friday
|
|
230
|
+
});
|
|
231
|
+
|
|
232
|
+
const description = recurrenceToString(weekly.rule);
|
|
233
|
+
// "Every week on Monday, Wednesday, Friday"
|
|
234
|
+
|
|
235
|
+
// Monthly on the 15th
|
|
236
|
+
const monthly = createRecurrence({
|
|
237
|
+
frequency: "monthly",
|
|
238
|
+
interval: 1,
|
|
239
|
+
startDate: new Date("2024-01-01"),
|
|
240
|
+
byMonthDay: [15],
|
|
241
|
+
until: new Date("2024-12-31"),
|
|
242
|
+
});
|
|
243
|
+
|
|
244
|
+
const occurrencesInRange = monthly.getOccurrencesBetween(
|
|
245
|
+
new Date("2024-03-01"),
|
|
246
|
+
new Date("2024-06-30")
|
|
247
|
+
);
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
### Countdown & Timer Utilities (NEW!)
|
|
251
|
+
|
|
252
|
+
```ts
|
|
253
|
+
import {
|
|
254
|
+
createCountdown,
|
|
255
|
+
getRemainingTime,
|
|
256
|
+
formatCountdown,
|
|
257
|
+
} from "ts-time-utils/countdown";
|
|
258
|
+
|
|
259
|
+
// Create a countdown timer
|
|
260
|
+
const countdown = createCountdown(new Date("2024-12-31T23:59:59"), {
|
|
261
|
+
onTick: (remaining) => {
|
|
262
|
+
console.log(`${remaining.days}d ${remaining.hours}h ${remaining.minutes}m`);
|
|
263
|
+
},
|
|
264
|
+
onComplete: () => {
|
|
265
|
+
console.log("Happy New Year!");
|
|
266
|
+
},
|
|
267
|
+
interval: 1000, // Update every second
|
|
268
|
+
});
|
|
269
|
+
|
|
270
|
+
countdown.start();
|
|
271
|
+
// Later...
|
|
272
|
+
countdown.stop();
|
|
273
|
+
|
|
274
|
+
// Get remaining time
|
|
275
|
+
const remaining = getRemainingTime(new Date("2024-12-31"));
|
|
276
|
+
console.log(`${remaining.days} days, ${remaining.hours} hours remaining`);
|
|
277
|
+
|
|
278
|
+
// Format countdown
|
|
279
|
+
const formatted = formatCountdown(new Date("2024-12-31"), {
|
|
280
|
+
units: ["days", "hours", "minutes"],
|
|
281
|
+
short: true,
|
|
282
|
+
});
|
|
283
|
+
// "45d 12h 30m"
|
|
284
|
+
|
|
285
|
+
// Progress tracking
|
|
286
|
+
import { getProgressPercentage } from "ts-time-utils/countdown";
|
|
287
|
+
|
|
288
|
+
const progress = getProgressPercentage(
|
|
289
|
+
new Date("2024-01-01"),
|
|
290
|
+
new Date("2024-12-31"),
|
|
291
|
+
new Date("2024-07-01")
|
|
292
|
+
);
|
|
293
|
+
console.log(`${progress}% complete`); // ~50%
|
|
294
|
+
```
|
|
295
|
+
|
|
296
|
+
### Date Range Utilities (NEW!)
|
|
297
|
+
|
|
298
|
+
```ts
|
|
299
|
+
import {
|
|
300
|
+
mergeDateRanges,
|
|
301
|
+
findGaps,
|
|
302
|
+
dateRangeOverlap,
|
|
303
|
+
splitRange,
|
|
304
|
+
} from "ts-time-utils/dateRange";
|
|
305
|
+
|
|
306
|
+
// Merge overlapping ranges
|
|
307
|
+
const ranges = [
|
|
308
|
+
{ start: new Date("2024-01-01"), end: new Date("2024-01-10") },
|
|
309
|
+
{ start: new Date("2024-01-05"), end: new Date("2024-01-15") },
|
|
310
|
+
{ start: new Date("2024-01-20"), end: new Date("2024-01-25") },
|
|
311
|
+
];
|
|
312
|
+
|
|
313
|
+
const merged = mergeDateRanges(ranges);
|
|
314
|
+
// [
|
|
315
|
+
// { start: Date('2024-01-01'), end: Date('2024-01-15') },
|
|
316
|
+
// { start: Date('2024-01-20'), end: Date('2024-01-25') }
|
|
317
|
+
// ]
|
|
318
|
+
|
|
319
|
+
// Find gaps between busy times
|
|
320
|
+
const busyTimes = [
|
|
321
|
+
{ start: new Date("2024-01-01T09:00"), end: new Date("2024-01-01T11:00") },
|
|
322
|
+
{ start: new Date("2024-01-01T14:00"), end: new Date("2024-01-01T16:00") },
|
|
323
|
+
];
|
|
324
|
+
|
|
325
|
+
const gaps = findGaps(busyTimes, {
|
|
326
|
+
start: new Date("2024-01-01T08:00"),
|
|
327
|
+
end: new Date("2024-01-01T18:00"),
|
|
328
|
+
});
|
|
329
|
+
// Returns available time slots
|
|
330
|
+
|
|
331
|
+
// Split into chunks
|
|
332
|
+
const range = {
|
|
333
|
+
start: new Date("2024-01-01"),
|
|
334
|
+
end: new Date("2024-01-31"),
|
|
335
|
+
};
|
|
336
|
+
|
|
337
|
+
const weeks = splitRange(range, 1, "week");
|
|
338
|
+
// Splits January into weekly chunks
|
|
339
|
+
|
|
340
|
+
// Check overlap
|
|
341
|
+
const overlap = dateRangeOverlap(
|
|
342
|
+
{ start: new Date("2024-01-01"), end: new Date("2024-01-15") },
|
|
343
|
+
{ start: new Date("2024-01-10"), end: new Date("2024-01-20") }
|
|
344
|
+
); // true
|
|
345
|
+
```
|
|
346
|
+
|
|
347
|
+
### Natural Language Parsing (NEW!)
|
|
348
|
+
|
|
349
|
+
```ts
|
|
350
|
+
import {
|
|
351
|
+
parseNaturalDate,
|
|
352
|
+
extractDatesFromText,
|
|
353
|
+
suggestDateFromContext,
|
|
354
|
+
} from "ts-time-utils/naturalLanguage";
|
|
355
|
+
|
|
356
|
+
// Parse natural language dates
|
|
357
|
+
parseNaturalDate("tomorrow at 3pm");
|
|
358
|
+
// Returns Date for tomorrow at 15:00
|
|
359
|
+
|
|
360
|
+
parseNaturalDate("next Friday");
|
|
361
|
+
// Returns Date for next Friday
|
|
362
|
+
|
|
363
|
+
parseNaturalDate("in 2 weeks");
|
|
364
|
+
// Returns Date 2 weeks from now
|
|
365
|
+
|
|
366
|
+
parseNaturalDate("3 days ago");
|
|
367
|
+
// Returns Date 3 days ago
|
|
368
|
+
|
|
369
|
+
// Extract dates from text
|
|
370
|
+
const text = "Meeting tomorrow at 3pm and lunch next Friday at noon";
|
|
371
|
+
const dates = extractDatesFromText(text);
|
|
372
|
+
// [
|
|
373
|
+
// { date: Date(...), text: 'tomorrow at 3pm', index: 8, confidence: 0.9 },
|
|
374
|
+
// { date: Date(...), text: 'next Friday at noon', index: 35, confidence: 0.85 }
|
|
375
|
+
// ]
|
|
376
|
+
|
|
377
|
+
// Context-aware suggestions
|
|
378
|
+
const suggestions = suggestDateFromContext("deadline is end of month");
|
|
379
|
+
// [{ date: Date(last day of current month), text: 'end of month', confidence: 0.85 }]
|
|
380
|
+
|
|
381
|
+
// Supported phrases:
|
|
382
|
+
// - "tomorrow", "yesterday", "today"
|
|
383
|
+
// - "next Monday", "last Friday"
|
|
384
|
+
// - "in 2 hours", "5 days ago"
|
|
385
|
+
// - "end of month/week/year" (or EOM/EOW/EOY)
|
|
386
|
+
// - "beginning of month/year"
|
|
387
|
+
```
|
|
388
|
+
|
|
389
|
+
### Duration Utilities
|
|
390
|
+
|
|
391
|
+
```ts
|
|
392
|
+
import {
|
|
393
|
+
Duration,
|
|
394
|
+
createDuration,
|
|
395
|
+
formatDurationString,
|
|
396
|
+
} from "ts-time-utils/duration";
|
|
397
|
+
|
|
398
|
+
// Create durations
|
|
399
|
+
const duration1 = Duration.fromHours(2.5); // 2.5 hours
|
|
400
|
+
const duration2 = new Duration({ hours: 1, minutes: 30 }); // 1.5 hours
|
|
401
|
+
const duration3 = Duration.fromString("1h 30m 45s"); // Parse from string
|
|
402
|
+
const duration4 = Duration.between(startDate, endDate); // From date range
|
|
403
|
+
|
|
404
|
+
// Arithmetic operations
|
|
405
|
+
const sum = duration1.add(duration2); // 4 hours
|
|
406
|
+
const diff = duration1.subtract(duration2); // 1 hour
|
|
407
|
+
const doubled = duration1.multiply(2); // 5 hours
|
|
408
|
+
const half = duration1.divide(2); // 1.25 hours
|
|
409
|
+
|
|
410
|
+
// Comparisons
|
|
411
|
+
duration1.equals(duration2); // false
|
|
412
|
+
duration1.greaterThan(duration2); // true
|
|
413
|
+
duration1.compareTo(duration2); // 1
|
|
414
|
+
|
|
415
|
+
// Conversions and formatting
|
|
416
|
+
duration1.hours; // 2.5
|
|
417
|
+
duration1.minutes; // 150
|
|
418
|
+
duration1.toString(); // "2h 30m"
|
|
419
|
+
formatDurationString(duration1, { long: true }); // "2 hours, 30 minutes"
|
|
420
|
+
|
|
421
|
+
// Utility functions with arrays
|
|
422
|
+
const durations = [duration1, duration2, duration3];
|
|
423
|
+
const max = maxDuration(...durations);
|
|
424
|
+
const total = sumDurations(...durations);
|
|
425
|
+
const average = averageDuration(...durations);
|
|
426
|
+
```
|
|
427
|
+
|
|
428
|
+
### Serialization Utilities
|
|
429
|
+
|
|
430
|
+
```ts
|
|
431
|
+
import {
|
|
432
|
+
serializeDate,
|
|
433
|
+
deserializeDate,
|
|
434
|
+
parseJSONWithDates,
|
|
435
|
+
stringifyWithDates,
|
|
436
|
+
toEpochTimestamp,
|
|
437
|
+
fromEpochTimestamp,
|
|
438
|
+
toDateObject,
|
|
439
|
+
fromDateObject,
|
|
440
|
+
} from "ts-time-utils/serialize";
|
|
441
|
+
|
|
442
|
+
// Serialize dates in different formats
|
|
443
|
+
const date = new Date("2025-09-14T12:30:45.123Z");
|
|
444
|
+
|
|
445
|
+
const isoString = serializeDate(date, { format: "iso" }); // "2025-09-14T12:30:45.123Z"
|
|
446
|
+
const epochMs = serializeDate(date, { format: "epoch" }); // 1757853045123
|
|
447
|
+
const dateObj = serializeDate(date, { format: "object" }); // {year: 2025, month: 9, ...}
|
|
448
|
+
const custom = serializeDate(date, {
|
|
449
|
+
format: "custom",
|
|
450
|
+
customFormat: "YYYY-MM-DD HH:mm:ss",
|
|
451
|
+
}); // "2025-09-14 12:30:45"
|
|
452
|
+
|
|
453
|
+
// Deserialize from various formats
|
|
454
|
+
const fromISO = deserializeDate("2025-09-14T12:30:45.123Z");
|
|
455
|
+
const fromEpoch = deserializeDate(1757853045123);
|
|
456
|
+
const fromObj = deserializeDate({
|
|
457
|
+
year: 2025,
|
|
458
|
+
month: 9,
|
|
459
|
+
day: 14,
|
|
460
|
+
hour: 12,
|
|
461
|
+
minute: 30,
|
|
462
|
+
second: 45,
|
|
463
|
+
millisecond: 123,
|
|
464
|
+
});
|
|
465
|
+
|
|
466
|
+
// Safe JSON handling with automatic date conversion
|
|
467
|
+
const data = {
|
|
468
|
+
name: "User",
|
|
469
|
+
createdAt: new Date(),
|
|
470
|
+
updatedAt: new Date(),
|
|
471
|
+
metadata: "other data",
|
|
472
|
+
};
|
|
473
|
+
|
|
474
|
+
// Stringify with automatic date serialization
|
|
475
|
+
const jsonString = stringifyWithDates(data, ["createdAt", "updatedAt"], {
|
|
476
|
+
format: "epoch",
|
|
477
|
+
});
|
|
478
|
+
// {"name":"User","createdAt":1757853045123,"updatedAt":1757853045123,"metadata":"other data"}
|
|
479
|
+
|
|
480
|
+
// Parse with automatic date restoration
|
|
481
|
+
const parsed = parseJSONWithDates(jsonString, ["createdAt", "updatedAt"]);
|
|
482
|
+
// parsed.createdAt and parsed.updatedAt are Date objects
|
|
483
|
+
|
|
484
|
+
// Epoch timestamp utilities
|
|
485
|
+
const timestamp = toEpochTimestamp(date, "seconds"); // 1757853045
|
|
486
|
+
const restoredDate = fromEpochTimestamp(timestamp, "seconds");
|
|
487
|
+
|
|
488
|
+
// Date object utilities (UTC-based)
|
|
489
|
+
const dateObject = toDateObject(date, true); // includes timezone
|
|
490
|
+
const reconstructed = fromDateObject(dateObject);
|
|
491
|
+
```
|
|
492
|
+
|
|
129
493
|
### Format Utilities
|
|
130
494
|
|
|
131
495
|
```ts
|
|
@@ -289,8 +653,198 @@ const week = thisWeek();
|
|
|
289
653
|
const quarter = quarterRange();
|
|
290
654
|
```
|
|
291
655
|
|
|
656
|
+
### Locale Utilities
|
|
657
|
+
|
|
658
|
+
```ts
|
|
659
|
+
import {
|
|
660
|
+
formatRelativeTime,
|
|
661
|
+
formatDateLocale,
|
|
662
|
+
formatTimeLocale,
|
|
663
|
+
formatDateTimeLocale,
|
|
664
|
+
registerLocale,
|
|
665
|
+
getLocaleConfig,
|
|
666
|
+
detectLocale,
|
|
667
|
+
getSupportedLocales,
|
|
668
|
+
} from "ts-time-utils/locale";
|
|
669
|
+
|
|
670
|
+
// Relative time formatting in multiple languages
|
|
671
|
+
const pastDate = new Date(Date.now() - 2 * 60 * 60 * 1000); // 2 hours ago
|
|
672
|
+
formatRelativeTime(pastDate, { locale: "en" }); // "2 hours ago"
|
|
673
|
+
formatRelativeTime(pastDate, { locale: "es" }); // "hace 2 horas"
|
|
674
|
+
formatRelativeTime(pastDate, { locale: "fr" }); // "il y a 2 heures"
|
|
675
|
+
formatRelativeTime(pastDate, { locale: "de" }); // "vor 2 Stunden"
|
|
676
|
+
formatRelativeTime(pastDate, { locale: "nl" }); // "2 uur geleden"
|
|
677
|
+
formatRelativeTime(pastDate, { locale: "it" }); // "2 ore fa"
|
|
678
|
+
formatRelativeTime(pastDate, { locale: "zh" }); // "2小时前"
|
|
679
|
+
formatRelativeTime(pastDate, { locale: "ja" }); // "2時間前"
|
|
680
|
+
formatRelativeTime(pastDate, { locale: "fa" }); // "2 ساعت پیش"
|
|
681
|
+
|
|
682
|
+
// Future dates
|
|
683
|
+
const futureDate = new Date(Date.now() + 3 * 24 * 60 * 60 * 1000);
|
|
684
|
+
formatRelativeTime(futureDate, { locale: "en" }); // "in 3 days"
|
|
685
|
+
formatRelativeTime(futureDate, { locale: "es" }); // "en 3 días"
|
|
686
|
+
formatRelativeTime(futureDate, { locale: "nl" }); // "over 3 dagen"
|
|
687
|
+
formatRelativeTime(futureDate, { locale: "it" }); // "tra 3 giorni"
|
|
688
|
+
formatRelativeTime(futureDate, { locale: "fa" }); // "3 روز دیگر"
|
|
689
|
+
|
|
690
|
+
// Relative time options
|
|
691
|
+
formatRelativeTime(pastDate, {
|
|
692
|
+
locale: "en",
|
|
693
|
+
maxUnit: "days", // Don't use units larger than days
|
|
694
|
+
minUnit: "minutes", // Don't use units smaller than minutes
|
|
695
|
+
precision: 1, // Show 1 decimal place: "2.0 hours ago"
|
|
696
|
+
short: true, // Use abbreviated format: "2h ago"
|
|
697
|
+
numeric: "auto", // Use words when appropriate: "yesterday"
|
|
698
|
+
});
|
|
699
|
+
|
|
700
|
+
// Date formatting
|
|
701
|
+
const date = new Date("2024-01-15T14:30:45Z");
|
|
702
|
+
formatDateLocale(date, "en", "medium"); // "Jan 15, 2024"
|
|
703
|
+
formatDateLocale(date, "es", "medium"); // "15 ene 2024"
|
|
704
|
+
formatDateLocale(date, "fr", "long"); // "15 janvier 2024"
|
|
705
|
+
formatDateLocale(date, "de", "short"); // "15.1.2024"
|
|
706
|
+
|
|
707
|
+
// Time formatting
|
|
708
|
+
formatTimeLocale(date, "en", "short"); // "2:30 PM"
|
|
709
|
+
formatTimeLocale(date, "de", "medium"); // "14:30:45"
|
|
710
|
+
formatTimeLocale(date, "fr", "long"); // "14:30:45 UTC"
|
|
711
|
+
|
|
712
|
+
// Combined date and time
|
|
713
|
+
formatDateTimeLocale(date, "en"); // "Jan 15, 2024 2:30:45 PM"
|
|
714
|
+
|
|
715
|
+
// Auto-detect locale from browser/system
|
|
716
|
+
const userLocale = detectLocale(); // e.g., 'en-US' or 'fr-FR'
|
|
717
|
+
formatRelativeTime(pastDate, { locale: userLocale });
|
|
718
|
+
|
|
719
|
+
// Get supported locales
|
|
720
|
+
const locales = getSupportedLocales();
|
|
721
|
+
// ['en', 'es', 'fr', 'de', 'zh', 'ja', ...]
|
|
722
|
+
|
|
723
|
+
// Register custom locale
|
|
724
|
+
registerLocale("custom", {
|
|
725
|
+
locale: "custom",
|
|
726
|
+
dateFormats: {
|
|
727
|
+
short: "M/d/yyyy",
|
|
728
|
+
medium: "MMM d, yyyy",
|
|
729
|
+
long: "MMMM d, yyyy",
|
|
730
|
+
full: "EEEE, MMMM d, yyyy",
|
|
731
|
+
},
|
|
732
|
+
timeFormats: {
|
|
733
|
+
short: "h:mm a",
|
|
734
|
+
medium: "h:mm:ss a",
|
|
735
|
+
long: "h:mm:ss a z",
|
|
736
|
+
full: "h:mm:ss a zzzz",
|
|
737
|
+
},
|
|
738
|
+
relativeTime: {
|
|
739
|
+
future: "in {0}",
|
|
740
|
+
past: "{0} ago",
|
|
741
|
+
units: {
|
|
742
|
+
second: "sec",
|
|
743
|
+
seconds: "secs",
|
|
744
|
+
minute: "min",
|
|
745
|
+
minutes: "mins",
|
|
746
|
+
hour: "hr",
|
|
747
|
+
hours: "hrs",
|
|
748
|
+
day: "day",
|
|
749
|
+
days: "days",
|
|
750
|
+
week: "wk",
|
|
751
|
+
weeks: "wks",
|
|
752
|
+
month: "mo",
|
|
753
|
+
months: "mos",
|
|
754
|
+
year: "yr",
|
|
755
|
+
years: "yrs",
|
|
756
|
+
},
|
|
757
|
+
},
|
|
758
|
+
calendar: {
|
|
759
|
+
weekStartsOn: 0, // Sunday
|
|
760
|
+
monthNames: ["Jan", "Feb", "Mar" /* ... */],
|
|
761
|
+
monthNamesShort: ["J", "F", "M" /* ... */],
|
|
762
|
+
dayNames: ["Sun", "Mon", "Tue" /* ... */],
|
|
763
|
+
dayNamesShort: ["S", "M", "T" /* ... */],
|
|
764
|
+
},
|
|
765
|
+
numbers: {
|
|
766
|
+
decimal: ".",
|
|
767
|
+
thousands: ",",
|
|
768
|
+
},
|
|
769
|
+
});
|
|
770
|
+
|
|
771
|
+
// Locale Conversion Utilities
|
|
772
|
+
import {
|
|
773
|
+
convertRelativeTime,
|
|
774
|
+
detectLocaleFromRelativeTime,
|
|
775
|
+
convertFormatPattern,
|
|
776
|
+
convertFormattedDate,
|
|
777
|
+
convertRelativeTimeArray,
|
|
778
|
+
compareLocaleFormats,
|
|
779
|
+
} from "ts-time-utils/locale";
|
|
780
|
+
|
|
781
|
+
// Convert relative time between locales
|
|
782
|
+
convertRelativeTime("2 hours ago", "en", "es"); // "hace 2 horas"
|
|
783
|
+
convertRelativeTime("hace 3 días", "es", "fr"); // "il y a 3 jours"
|
|
784
|
+
convertRelativeTime("2h ago", "en", "de"); // "vor 2h"
|
|
785
|
+
convertRelativeTime("2 hours ago", "en", "nl"); // "2 uur geleden"
|
|
786
|
+
convertRelativeTime("2 hours ago", "en", "it"); // "2 ore fa"
|
|
787
|
+
convertRelativeTime("2 hours ago", "en", "fa"); // "2 ساعت پیش"
|
|
788
|
+
convertRelativeTime("2 ساعت پیش", "fa", "en"); // "2 hours ago"
|
|
789
|
+
|
|
790
|
+
// Detect locale from formatted text
|
|
791
|
+
detectLocaleFromRelativeTime("2 hours ago"); // "en"
|
|
792
|
+
detectLocaleFromRelativeTime("hace 2 horas"); // "es"
|
|
793
|
+
detectLocaleFromRelativeTime("il y a 2 heures"); // "fr"
|
|
794
|
+
detectLocaleFromRelativeTime("2 uur geleden"); // "nl"
|
|
795
|
+
detectLocaleFromRelativeTime("2 ore fa"); // "it"
|
|
796
|
+
detectLocaleFromRelativeTime("2 ساعت پیش"); // "fa"
|
|
797
|
+
detectLocaleFromRelativeTime("vor 2 Stunden"); // "de"
|
|
798
|
+
|
|
799
|
+
// Convert date format patterns between locales
|
|
800
|
+
convertFormatPattern("M/d/yyyy", "en", "de"); // "dd.MM.yyyy"
|
|
801
|
+
convertFormatPattern("MMM d, yyyy", "en", "fr", "long"); // "d MMMM yyyy"
|
|
802
|
+
|
|
803
|
+
// Convert formatted dates between locales
|
|
804
|
+
convertFormattedDate("Jan 15, 2024", "en", "es"); // "15 ene 2024"
|
|
805
|
+
convertFormattedDate("15. Januar 2024", "de", "en"); // "Jan 15, 2024"
|
|
806
|
+
|
|
807
|
+
// Bulk conversion of relative time arrays
|
|
808
|
+
const englishTimes = ["2 hours ago", "in 3 days", "1 week ago"];
|
|
809
|
+
convertRelativeTimeArray(englishTimes, "en", "es");
|
|
810
|
+
// ["hace 2 horas", "en 3 días", "hace 1 semana"]
|
|
811
|
+
|
|
812
|
+
// Compare format differences between locales
|
|
813
|
+
const comparison = compareLocaleFormats("en", "de");
|
|
814
|
+
console.log(comparison.dateFormats.short);
|
|
815
|
+
// { locale1: "M/d/yyyy", locale2: "dd.MM.yyyy" }
|
|
816
|
+
console.log(comparison.weekStartsOn);
|
|
817
|
+
// { locale1: 0, locale2: 1 } // Sunday vs Monday
|
|
818
|
+
```
|
|
819
|
+
|
|
292
820
|
## 📊 API Reference
|
|
293
821
|
|
|
822
|
+
### Duration Functions
|
|
823
|
+
|
|
824
|
+
- `Duration` class - Immutable duration with full arithmetic support
|
|
825
|
+
- `createDuration(input)` - Create duration from number, object, or string
|
|
826
|
+
- `Duration.fromHours/Minutes/Seconds/Days/Weeks(n)` - Create from specific units
|
|
827
|
+
- `Duration.fromString(str)` - Parse from string like "1h 30m 45s"
|
|
828
|
+
- `Duration.between(start, end)` - Create from date range
|
|
829
|
+
- `duration.add/subtract/multiply/divide()` - Arithmetic operations
|
|
830
|
+
- `duration.equals/greaterThan/lessThan()` - Comparison methods
|
|
831
|
+
- `formatDurationString(duration, options?)` - Format to readable string
|
|
832
|
+
- `maxDuration/minDuration(...durations)` - Find extremes
|
|
833
|
+
- `sumDurations/averageDuration(...durations)` - Aggregate operations
|
|
834
|
+
|
|
835
|
+
### Serialization Functions
|
|
836
|
+
|
|
837
|
+
- `serializeDate(date, options?)` - Serialize date to various formats (ISO, epoch, object, custom)
|
|
838
|
+
- `deserializeDate(serialized, options?)` - Deserialize from string, number, or object
|
|
839
|
+
- `parseJSONWithDates(jsonString, dateKeys?, options?)` - Parse JSON with automatic date conversion
|
|
840
|
+
- `stringifyWithDates(obj, dateKeys?, options?)` - Stringify JSON with automatic date serialization
|
|
841
|
+
- `createDateReviver/createDateReplacer(dateKeys?, options?)` - Create JSON reviver/replacer functions
|
|
842
|
+
- `toEpochTimestamp/fromEpochTimestamp(input, precision?)` - Convert to/from epoch timestamps
|
|
843
|
+
- `toDateObject/fromDateObject(input)` - Convert to/from safe object representation
|
|
844
|
+
- `isValidISODateString/isValidEpochTimestamp(input)` - Validation utilities
|
|
845
|
+
- `cloneDate(date)` - Safe date cloning
|
|
846
|
+
- `datesEqual(date1, date2, precision?)` - Compare dates with precision control
|
|
847
|
+
|
|
294
848
|
### Format Functions
|
|
295
849
|
|
|
296
850
|
- `formatDuration(ms, options?)` - Format milliseconds to readable duration
|
|
@@ -319,6 +873,41 @@ const quarter = quarterRange();
|
|
|
319
873
|
- `isValidTimeString(time)` - Validate HH:MM time format
|
|
320
874
|
- `isValidISOString(dateString)` - Validate ISO 8601 date string
|
|
321
875
|
|
|
876
|
+
### Locale Functions
|
|
877
|
+
|
|
878
|
+
- `formatRelativeTime(date, options?)` - Format relative time with locale support
|
|
879
|
+
- Options: `locale`, `maxUnit`, `minUnit`, `precision`, `short`, `numeric`, `style`
|
|
880
|
+
- Supports 30+ locales: en, es, fr, de, it, pt, nl, sv, da, no, fi, pl, cs, sk, hu, ro, bg, hr, sl, et, lv, lt, ru, uk, tr, ar, he, hi, th, ko, zh, ja
|
|
881
|
+
- `formatDateLocale(date, locale?, style?)` - Format date in locale-specific format
|
|
882
|
+
- Styles: 'short', 'medium', 'long', 'full'
|
|
883
|
+
- `formatTimeLocale(date, locale?, style?)` - Format time in locale-specific format
|
|
884
|
+
- `formatDateTimeLocale(date, locale?, dateStyle?, timeStyle?)` - Format both date and time
|
|
885
|
+
- `registerLocale(locale, config)` - Register a custom locale configuration
|
|
886
|
+
- `getLocaleConfig(locale)` - Get configuration for a specific locale
|
|
887
|
+
- `detectLocale(fallback?)` - Auto-detect system/browser locale
|
|
888
|
+
- `getSupportedLocales()` - Get array of all supported locale codes
|
|
889
|
+
- `getMonthNames(locale?, short?)` - Get localized month names
|
|
890
|
+
- `getDayNames(locale?, short?)` - Get localized day names
|
|
891
|
+
- `getBestMatchingLocale(preferences, fallback?)` - Find best matching locale from preferences
|
|
892
|
+
|
|
893
|
+
#### Locale Conversion Functions
|
|
894
|
+
|
|
895
|
+
- `convertRelativeTime(text, fromLocale, toLocale)` - Convert relative time between locales
|
|
896
|
+
- Example: `convertRelativeTime("2 hours ago", "en", "es")` → `"hace 2 horas"`
|
|
897
|
+
- Example: `convertRelativeTime("2 hours ago", "en", "nl")` → `"2 uur geleden"`
|
|
898
|
+
- Example: `convertRelativeTime("2 hours ago", "en", "it")` → `"2 ore fa"`
|
|
899
|
+
- Example: `convertRelativeTime("2 hours ago", "en", "fa")` → `"2 ساعت پیش"`
|
|
900
|
+
- `detectLocaleFromRelativeTime(text)` - Detect locale from relative time string
|
|
901
|
+
- Returns most likely locale or null if detection fails
|
|
902
|
+
- `convertFormatPattern(pattern, fromLocale, toLocale, style?)` - Convert date format patterns
|
|
903
|
+
- Maps common patterns between locales or uses target locale's style
|
|
904
|
+
- `convertFormattedDate(formattedDate, fromLocale, toLocale, targetStyle?)` - Convert formatted dates
|
|
905
|
+
- Parses date in source locale and reformats in target locale
|
|
906
|
+
- `convertRelativeTimeArray(array, fromLocale, toLocale)` - Bulk convert relative time arrays
|
|
907
|
+
- Returns array with same length, null for unparseable strings
|
|
908
|
+
- `compareLocaleFormats(locale1, locale2)` - Compare format differences between locales
|
|
909
|
+
- Returns object with dateFormats, timeFormats, and weekStartsOn comparisons
|
|
910
|
+
|
|
322
911
|
## 🛠️ Development
|
|
323
912
|
|
|
324
913
|
```bash
|
package/dist/age.d.ts
CHANGED
|
@@ -1,14 +1,5 @@
|
|
|
1
1
|
import { TimeUnit } from './constants.js';
|
|
2
|
-
|
|
3
|
-
* Age calculation result
|
|
4
|
-
*/
|
|
5
|
-
export interface AgeResult {
|
|
6
|
-
years: number;
|
|
7
|
-
months: number;
|
|
8
|
-
days: number;
|
|
9
|
-
totalDays: number;
|
|
10
|
-
totalMonths: number;
|
|
11
|
-
}
|
|
2
|
+
import type { AgeResult } from './types.js';
|
|
12
3
|
/**
|
|
13
4
|
* Calculate detailed age from birth date
|
|
14
5
|
* @param birthDate - date of birth
|
package/dist/age.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"age.d.ts","sourceRoot":"","sources":["../src/age.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;
|
|
1
|
+
{"version":3,"file":"age.d.ts","sourceRoot":"","sources":["../src/age.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAC1C,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAE5C;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,SAAS,EAAE,IAAI,EAAE,aAAa,GAAE,IAAiB,GAAG,SAAS,CAyBzF;AAED;;;;;GAKG;AACH,wBAAgB,aAAa,CAC3B,SAAS,EAAE,IAAI,EACf,IAAI,EAAE,QAAQ,EACd,aAAa,GAAE,IAAiB,GAC/B,MAAM,CAsBR;AAED;;;;GAIG;AACH,wBAAgB,YAAY,CAC1B,SAAS,EAAE,IAAI,EACf,aAAa,GAAE,IAAiB,GAC/B,QAAQ,GAAG,OAAO,GAAG,MAAM,GAAG,OAAO,GAAG,QAAQ,CAQlD;AAED;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,SAAS,EAAE,IAAI,EAAE,aAAa,GAAE,IAAiB,GAAG,IAAI,CAYvF;AAED;;;;GAIG;AACH,wBAAgB,oBAAoB,CAAC,SAAS,EAAE,IAAI,EAAE,aAAa,GAAE,IAAiB,GAAG,MAAM,CAG9F;AAED;;;;GAIG;AACH,wBAAgB,UAAU,CAAC,SAAS,EAAE,IAAI,EAAE,aAAa,GAAE,IAAiB,GAAG,OAAO,CAQrF"}
|
package/dist/calculate.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"calculate.d.ts","sourceRoot":"","sources":["../src/calculate.ts"],"names":[],"mappings":"AAAA,OAAO,EAQL,QAAQ,EACT,MAAM,gBAAgB,CAAC;AAExB;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAC/B,KAAK,EAAE,IAAI,EACX,KAAK,EAAE,IAAI,EACX,IAAI,GAAE,QAAyB,EAC/B,OAAO,GAAE,OAAc,GACtB,MAAM,CAkCR;AAED;;;;;GAKG;AACH,wBAAgB,OAAO,CAAC,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,QAAQ,GAAG,IAAI,
|
|
1
|
+
{"version":3,"file":"calculate.d.ts","sourceRoot":"","sources":["../src/calculate.ts"],"names":[],"mappings":"AAAA,OAAO,EAQL,QAAQ,EACT,MAAM,gBAAgB,CAAC;AAExB;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAC/B,KAAK,EAAE,IAAI,EACX,KAAK,EAAE,IAAI,EACX,IAAI,GAAE,QAAyB,EAC/B,OAAO,GAAE,OAAc,GACtB,MAAM,CAkCR;AAED;;;;;GAKG;AACH,wBAAgB,OAAO,CAAC,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,QAAQ,GAAG,IAAI,CAgDxE;AAED;;;;;GAKG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,QAAQ,GAAG,IAAI,CAE7E;AAED;;;;GAIG;AACH,wBAAgB,OAAO,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,GAAG,MAAM,GAAG,OAAO,GAAG,MAAM,GAAG,MAAM,GAAG,QAAQ,GAAG,IAAI,CA4BrG;AAED;;;;GAIG;AACH,wBAAgB,KAAK,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,GAAG,MAAM,GAAG,OAAO,GAAG,MAAM,GAAG,MAAM,GAAG,QAAQ,GAAG,IAAI,CA4BnG;AAED;;;;;GAKG;AACH,wBAAgB,SAAS,CAAC,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,GAAG,EAAE,IAAI,GAAG,OAAO,CAGrE;AAED;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,SAAS,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,GAAG,MAAM,CAa1E"}
|