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.
Files changed (100) hide show
  1. package/README.md +590 -1
  2. package/dist/age.d.ts +1 -10
  3. package/dist/age.d.ts.map +1 -1
  4. package/dist/calculate.d.ts.map +1 -1
  5. package/dist/calculate.js +24 -10
  6. package/dist/constants.d.ts +2 -21
  7. package/dist/constants.d.ts.map +1 -1
  8. package/dist/constants.js +12 -13
  9. package/dist/countdown.d.ts +217 -0
  10. package/dist/countdown.d.ts.map +1 -0
  11. package/dist/countdown.js +298 -0
  12. package/dist/dateRange.d.ts +266 -0
  13. package/dist/dateRange.d.ts.map +1 -0
  14. package/dist/dateRange.js +433 -0
  15. package/dist/duration.d.ts +171 -0
  16. package/dist/duration.d.ts.map +1 -0
  17. package/dist/duration.js +382 -0
  18. package/dist/esm/age.d.ts +1 -10
  19. package/dist/esm/age.d.ts.map +1 -1
  20. package/dist/esm/calculate.d.ts.map +1 -1
  21. package/dist/esm/calculate.js +24 -10
  22. package/dist/esm/constants.d.ts +2 -21
  23. package/dist/esm/constants.d.ts.map +1 -1
  24. package/dist/esm/constants.js +12 -13
  25. package/dist/esm/countdown.d.ts +217 -0
  26. package/dist/esm/countdown.d.ts.map +1 -0
  27. package/dist/esm/countdown.js +298 -0
  28. package/dist/esm/dateRange.d.ts +266 -0
  29. package/dist/esm/dateRange.d.ts.map +1 -0
  30. package/dist/esm/dateRange.js +433 -0
  31. package/dist/esm/duration.d.ts +171 -0
  32. package/dist/esm/duration.d.ts.map +1 -0
  33. package/dist/esm/duration.js +382 -0
  34. package/dist/esm/format.d.ts.map +1 -1
  35. package/dist/esm/index.d.ts +14 -6
  36. package/dist/esm/index.d.ts.map +1 -1
  37. package/dist/esm/index.js +16 -0
  38. package/dist/esm/interval.d.ts +3 -6
  39. package/dist/esm/interval.d.ts.map +1 -1
  40. package/dist/esm/locale.d.ts +94 -0
  41. package/dist/esm/locale.d.ts.map +1 -0
  42. package/dist/esm/locale.js +1087 -0
  43. package/dist/esm/naturalLanguage.d.ts +107 -0
  44. package/dist/esm/naturalLanguage.d.ts.map +1 -0
  45. package/dist/esm/naturalLanguage.js +344 -0
  46. package/dist/esm/performance.d.ts +2 -9
  47. package/dist/esm/performance.d.ts.map +1 -1
  48. package/dist/esm/performance.js +7 -8
  49. package/dist/esm/rangePresets.d.ts +7 -8
  50. package/dist/esm/rangePresets.d.ts.map +1 -1
  51. package/dist/esm/rangePresets.js +11 -9
  52. package/dist/esm/recurrence.d.ts +149 -0
  53. package/dist/esm/recurrence.d.ts.map +1 -0
  54. package/dist/esm/recurrence.js +404 -0
  55. package/dist/esm/serialize.d.ts +73 -0
  56. package/dist/esm/serialize.d.ts.map +1 -0
  57. package/dist/esm/serialize.js +365 -0
  58. package/dist/esm/timezone.d.ts +2 -6
  59. package/dist/esm/timezone.d.ts.map +1 -1
  60. package/dist/esm/timezone.js +1 -1
  61. package/dist/esm/types.d.ts +250 -0
  62. package/dist/esm/types.d.ts.map +1 -0
  63. package/dist/esm/types.js +25 -0
  64. package/dist/esm/workingHours.d.ts +4 -13
  65. package/dist/esm/workingHours.d.ts.map +1 -1
  66. package/dist/esm/workingHours.js +3 -1
  67. package/dist/format.d.ts.map +1 -1
  68. package/dist/index.d.ts +14 -6
  69. package/dist/index.d.ts.map +1 -1
  70. package/dist/index.js +16 -0
  71. package/dist/interval.d.ts +3 -6
  72. package/dist/interval.d.ts.map +1 -1
  73. package/dist/locale.d.ts +94 -0
  74. package/dist/locale.d.ts.map +1 -0
  75. package/dist/locale.js +1087 -0
  76. package/dist/naturalLanguage.d.ts +107 -0
  77. package/dist/naturalLanguage.d.ts.map +1 -0
  78. package/dist/naturalLanguage.js +344 -0
  79. package/dist/performance.d.ts +2 -9
  80. package/dist/performance.d.ts.map +1 -1
  81. package/dist/performance.js +7 -8
  82. package/dist/rangePresets.d.ts +7 -8
  83. package/dist/rangePresets.d.ts.map +1 -1
  84. package/dist/rangePresets.js +11 -9
  85. package/dist/recurrence.d.ts +149 -0
  86. package/dist/recurrence.d.ts.map +1 -0
  87. package/dist/recurrence.js +404 -0
  88. package/dist/serialize.d.ts +73 -0
  89. package/dist/serialize.d.ts.map +1 -0
  90. package/dist/serialize.js +365 -0
  91. package/dist/timezone.d.ts +2 -6
  92. package/dist/timezone.d.ts.map +1 -1
  93. package/dist/timezone.js +1 -1
  94. package/dist/types.d.ts +250 -0
  95. package/dist/types.d.ts.map +1 -0
  96. package/dist/types.js +25 -0
  97. package/dist/workingHours.d.ts +4 -13
  98. package/dist/workingHours.d.ts.map +1 -1
  99. package/dist/workingHours.js +3 -1
  100. 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** - 12 utility categories with 70+ functions
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;AAE1C;;GAEG;AACH,MAAM,WAAW,SAAS;IACxB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;CACrB;AAED;;;;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"}
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"}
@@ -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,CAmCxE;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"}
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"}