ts-time-utils 3.0.4 → 4.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 (86) hide show
  1. package/README.md +186 -6
  2. package/dist/calculate.d.ts +25 -0
  3. package/dist/calculate.d.ts.map +1 -1
  4. package/dist/calculate.js +125 -0
  5. package/dist/calendar.d.ts +45 -0
  6. package/dist/calendar.d.ts.map +1 -1
  7. package/dist/calendar.js +68 -0
  8. package/dist/calendars.d.ts +156 -0
  9. package/dist/calendars.d.ts.map +1 -0
  10. package/dist/calendars.js +348 -0
  11. package/dist/compare.d.ts +27 -0
  12. package/dist/compare.d.ts.map +1 -1
  13. package/dist/compare.js +46 -0
  14. package/dist/esm/calculate.d.ts +25 -0
  15. package/dist/esm/calculate.d.ts.map +1 -1
  16. package/dist/esm/calculate.js +125 -0
  17. package/dist/esm/calendar.d.ts +45 -0
  18. package/dist/esm/calendar.d.ts.map +1 -1
  19. package/dist/esm/calendar.js +68 -0
  20. package/dist/esm/calendars.d.ts +156 -0
  21. package/dist/esm/calendars.d.ts.map +1 -0
  22. package/dist/esm/calendars.js +348 -0
  23. package/dist/esm/compare.d.ts +27 -0
  24. package/dist/esm/compare.d.ts.map +1 -1
  25. package/dist/esm/compare.js +46 -0
  26. package/dist/esm/finance.d.ts +236 -0
  27. package/dist/esm/finance.d.ts.map +1 -0
  28. package/dist/esm/finance.js +495 -0
  29. package/dist/esm/healthcare.d.ts +260 -0
  30. package/dist/esm/healthcare.d.ts.map +1 -0
  31. package/dist/esm/healthcare.js +447 -0
  32. package/dist/esm/holidays.d.ts +11 -1
  33. package/dist/esm/holidays.d.ts.map +1 -1
  34. package/dist/esm/holidays.js +220 -1
  35. package/dist/esm/index.d.ts +19 -7
  36. package/dist/esm/index.d.ts.map +1 -1
  37. package/dist/esm/index.js +23 -9
  38. package/dist/esm/iterate.d.ts +55 -0
  39. package/dist/esm/iterate.d.ts.map +1 -1
  40. package/dist/esm/iterate.js +86 -0
  41. package/dist/esm/locale.d.ts +53 -0
  42. package/dist/esm/locale.d.ts.map +1 -1
  43. package/dist/esm/locale.js +141 -0
  44. package/dist/esm/precision.d.ts +225 -0
  45. package/dist/esm/precision.d.ts.map +1 -0
  46. package/dist/esm/precision.js +491 -0
  47. package/dist/esm/scheduling.d.ts +206 -0
  48. package/dist/esm/scheduling.d.ts.map +1 -0
  49. package/dist/esm/scheduling.js +329 -0
  50. package/dist/esm/temporal.d.ts +237 -0
  51. package/dist/esm/temporal.d.ts.map +1 -0
  52. package/dist/esm/temporal.js +660 -0
  53. package/dist/esm/validate.d.ts +30 -0
  54. package/dist/esm/validate.d.ts.map +1 -1
  55. package/dist/esm/validate.js +48 -0
  56. package/dist/finance.d.ts +236 -0
  57. package/dist/finance.d.ts.map +1 -0
  58. package/dist/finance.js +495 -0
  59. package/dist/healthcare.d.ts +260 -0
  60. package/dist/healthcare.d.ts.map +1 -0
  61. package/dist/healthcare.js +447 -0
  62. package/dist/holidays.d.ts +11 -1
  63. package/dist/holidays.d.ts.map +1 -1
  64. package/dist/holidays.js +220 -1
  65. package/dist/index.d.ts +19 -7
  66. package/dist/index.d.ts.map +1 -1
  67. package/dist/index.js +23 -9
  68. package/dist/iterate.d.ts +55 -0
  69. package/dist/iterate.d.ts.map +1 -1
  70. package/dist/iterate.js +86 -0
  71. package/dist/locale.d.ts +53 -0
  72. package/dist/locale.d.ts.map +1 -1
  73. package/dist/locale.js +141 -0
  74. package/dist/precision.d.ts +225 -0
  75. package/dist/precision.d.ts.map +1 -0
  76. package/dist/precision.js +491 -0
  77. package/dist/scheduling.d.ts +206 -0
  78. package/dist/scheduling.d.ts.map +1 -0
  79. package/dist/scheduling.js +329 -0
  80. package/dist/temporal.d.ts +237 -0
  81. package/dist/temporal.d.ts.map +1 -0
  82. package/dist/temporal.js +660 -0
  83. package/dist/validate.d.ts +30 -0
  84. package/dist/validate.d.ts.map +1 -1
  85. package/dist/validate.js +48 -0
  86. package/package.json +31 -1
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # ts-time-utils
2
2
 
3
- A comprehensive TypeScript utility library for time, dates, durations, and calendar operations. Zero dependencies, full tree-shaking support, 320+ functions across 26 categories.
3
+ A comprehensive TypeScript utility library for time, dates, durations, and calendar operations. Zero dependencies, full tree-shaking support, 430+ functions across 32 categories.
4
4
 
5
5
  [![npm version](https://img.shields.io/npm/v/ts-time-utils.svg)](https://www.npmjs.com/package/ts-time-utils)
6
6
  [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT)
@@ -12,7 +12,7 @@ A comprehensive TypeScript utility library for time, dates, durations, and calen
12
12
  - **Lightweight** — Import only what you need with tree-shaking support
13
13
  - **Zero dependencies** — Pure TypeScript, no external packages
14
14
  - **Type-safe** — Full TypeScript support with IntelliSense
15
- - **Comprehensive** — 320+ functions across 26 utility categories
15
+ - **Comprehensive** — 430+ functions across 32 utility categories
16
16
  - **Fluent API** — Chain operations with the `chain()` API
17
17
  - **Extensible** — Plugin system for custom functionality
18
18
 
@@ -256,7 +256,7 @@ extractDatesFromText('Meeting tomorrow at 3pm');
256
256
 
257
257
  ### International Holidays
258
258
 
259
- Public holidays for 10 countries.
259
+ Public holidays for 20 countries.
260
260
 
261
261
  ```ts
262
262
  import { getHolidays, isHoliday, getNextHoliday } from 'ts-time-utils/holidays';
@@ -266,7 +266,8 @@ getHolidays(2025, 'DE'); // German holidays
266
266
  isHoliday(date, 'CA'); // Is Canadian holiday?
267
267
  getNextHoliday(date, 'AU'); // Next Australian holiday
268
268
 
269
- // Supported: UK, NL, DE, CA, AU, IT, ES, CN, IN, US
269
+ // Supported: UK, NL, DE, CA, AU, IT, ES, CN, IN, US,
270
+ // JP, FR, BR, MX, KR, SG, PL, SE, BE, CH
270
271
  ```
271
272
 
272
273
  ### Locale
@@ -381,6 +382,69 @@ thisWeek(); // Current week
381
382
  thisMonth(); // Current month
382
383
  ```
383
384
 
385
+ ### Non-Gregorian Calendars
386
+
387
+ Convert dates between calendar systems using Intl.DateTimeFormat.
388
+
389
+ ```ts
390
+ import { toHebrewDate, toIslamicDate, toJapaneseDate, getChineseZodiac } from 'ts-time-utils/calendars';
391
+
392
+ toHebrewDate(new Date()); // { year: 5785, month: 4, day: 23, calendar: 'hebrew' }
393
+ toIslamicDate(new Date()); // { year: 1446, month: 7, day: 1, calendar: 'islamic-umalqura' }
394
+ toJapaneseDate(new Date()); // { year: 6, era: 'Reiwa', calendar: 'japanese' }
395
+ getChineseZodiac(2024); // 'Dragon'
396
+
397
+ // Supported: Hebrew, Islamic, Buddhist, Japanese, Persian, Chinese
398
+ ```
399
+
400
+ ### Temporal API Compatibility
401
+
402
+ Future-proof with Temporal-like objects that work with native Date.
403
+
404
+ ```ts
405
+ import { toPlainDate, toPlainDateTime, toZonedDateTime, toInstant } from 'ts-time-utils/temporal';
406
+
407
+ const date = toPlainDate(2024, 3, 25);
408
+ date.add({ days: 7 }); // PlainDate
409
+ date.until(otherDate); // Duration
410
+ date.dayOfWeek; // 1 (Monday, ISO)
411
+
412
+ const zdt = toZonedDateTime(new Date(), 'America/New_York');
413
+ zdt.hour; // Hour in that timezone
414
+ zdt.toInstant(); // Epoch-based Instant
415
+
416
+ const instant = toInstant(Date.now());
417
+ instant.toZonedDateTime('UTC'); // Convert to any timezone
418
+ ```
419
+
420
+ ### High-Precision Utilities
421
+
422
+ Nanosecond timestamps, BigInt support, DST handling, and leap seconds.
423
+
424
+ ```ts
425
+ import {
426
+ createNanosecondTimestamp, nowNanoseconds,
427
+ toBigIntMs, ValidDate, isInDSTGap, leapSecondsBetween
428
+ } from 'ts-time-utils/precision';
429
+
430
+ // Nanosecond precision
431
+ const ts = createNanosecondTimestamp(Date.now(), 500000);
432
+ ts.totalNanoseconds; // BigInt
433
+
434
+ // BigInt timestamps for large date ranges
435
+ toBigIntMs(new Date()); // BigInt milliseconds
436
+
437
+ // DST transition detection
438
+ isInDSTGap(new Date('2024-03-10T02:30:00'));
439
+
440
+ // Validated dates (never invalid)
441
+ const valid = ValidDate.from(date); // Throws if invalid
442
+ const maybe = ValidDate.tryFrom(date); // Returns null if invalid
443
+
444
+ // Leap second awareness
445
+ leapSecondsBetween(date1, date2); // Number of leap seconds
446
+ ```
447
+
384
448
  ### Parse
385
449
 
386
450
  Date parsing from various formats.
@@ -394,6 +458,98 @@ parseTime('2:30 PM'); // { hour: 14, minute: 30 }
394
458
  autoDetectFormat('2025-09-14'); // 'YYYY-MM-DD'
395
459
  ```
396
460
 
461
+ ### Scheduling
462
+
463
+ Appointment slots, availability, and booking conflict detection.
464
+
465
+ ```ts
466
+ import {
467
+ generateSlots, getAvailableSlots, findNextAvailable,
468
+ hasConflict, findConflicts, mergeBookings
469
+ } from 'ts-time-utils/scheduling';
470
+
471
+ // Generate 30-min slots for a day
472
+ const slots = generateSlots(new Date(), {
473
+ slotDuration: 30,
474
+ workingHours: { startTime: { hour: 9, minute: 0 }, endTime: { hour: 17, minute: 0 } }
475
+ });
476
+
477
+ // Find available slots (excluding existing bookings)
478
+ const available = getAvailableSlots(new Date(), existingBookings, config);
479
+
480
+ // Find next available slot of specific duration
481
+ findNextAvailable(new Date(), bookings, 60, config); // 60-min slot
482
+
483
+ // Check for conflicts
484
+ hasConflict(bookings, proposedSlot); // true/false
485
+ findConflicts(bookings, proposedSlot); // Array of conflicting bookings
486
+
487
+ // Merge adjacent bookings
488
+ mergeBookings(bookings);
489
+ ```
490
+
491
+ ### Finance
492
+
493
+ Market hours, trading days, and settlement date calculations.
494
+
495
+ ```ts
496
+ import {
497
+ isMarketOpen, isTradingDay, getMarketHours,
498
+ getNextMarketOpen, addTradingDays, getSettlementDate,
499
+ eachTradingDay, getOptionsExpiration
500
+ } from 'ts-time-utils/finance';
501
+
502
+ // Check market status
503
+ isMarketOpen(new Date(), 'NYSE'); // Is NYSE open right now?
504
+ isTradingDay(new Date(), 'NASDAQ'); // Is today a trading day?
505
+ getMarketHours('NYSE'); // { open: {hour:9,minute:30}, close: {hour:16,minute:0} }
506
+
507
+ // Market timing
508
+ getNextMarketOpen(new Date()); // Next market open time
509
+ addTradingDays(new Date(), 5); // 5 trading days from now
510
+
511
+ // Settlement (T+2, etc.)
512
+ getSettlementDate(tradeDate, 2); // T+2 settlement date
513
+
514
+ // Iterate trading days
515
+ eachTradingDay(start, end); // Array of trading days
516
+
517
+ // Options expiration (3rd Friday)
518
+ getOptionsExpiration(2025, 3); // March 2025 expiration
519
+ ```
520
+
521
+ ### Healthcare
522
+
523
+ Medication scheduling, shift patterns, and compliance windows.
524
+
525
+ ```ts
526
+ import {
527
+ getMedicationTimes, getNextMedicationTime, parseMedicationFrequency,
528
+ generateShiftSchedule, getShiftForTime, isOnShift,
529
+ createOnCallRotation, getOnCallStaff,
530
+ getComplianceDeadline, timeUntilDeadline
531
+ } from 'ts-time-utils/healthcare';
532
+
533
+ // Medication times (BID = twice daily, TID = 3x daily, etc.)
534
+ getMedicationTimes(new Date(), 'BID'); // [8am, 8pm] (default wake/sleep times)
535
+ getMedicationTimes(new Date(), 'q6h'); // Every 6 hours
536
+ getNextMedicationTime(now, 'TID'); // Next scheduled dose
537
+ parseMedicationFrequency('twice daily'); // 'BID'
538
+
539
+ // Shift scheduling
540
+ generateShiftSchedule(start, end, { pattern: '12hr', startTime: { hour: 7, minute: 0 } });
541
+ getShiftForTime(now, shiftConfig); // Current shift
542
+ isOnShift(now, shiftStart, config); // Is within shift?
543
+
544
+ // On-call rotations
545
+ const rotation = createOnCallRotation(start, end, ['Dr. Smith', 'Dr. Jones'], 24);
546
+ getOnCallStaff(now, rotation); // Who's on call?
547
+
548
+ // Compliance windows
549
+ getComplianceDeadline(eventDate, 72); // 72-hour window
550
+ timeUntilDeadline(eventDate, deadline); // Duration remaining
551
+ ```
552
+
397
553
  ---
398
554
 
399
555
  ## Plugin System
@@ -432,12 +588,15 @@ For complete API documentation, see the [Playground & Docs](https://ts-time-util
432
588
  | Module | Description |
433
589
  |--------|-------------|
434
590
  | `format` | Duration formatting, time ago, date patterns |
435
- | `calculate` | Date arithmetic, differences, period boundaries |
591
+ | `calculate` | Date arithmetic, differences, rounding |
436
592
  | `validate` | Date validation, comparisons, type checks |
437
593
  | `duration` | Immutable Duration class with arithmetic |
438
594
  | `chain` | Fluent chainable API |
439
595
  | `timezone` | Timezone conversions, DST handling |
440
596
  | `calendar` | ISO weeks, quarters, holidays, grids |
597
+ | `calendars` | Non-Gregorian calendars (Hebrew, Islamic, etc.) |
598
+ | `temporal` | Temporal API compatibility layer |
599
+ | `precision` | Nanoseconds, BigInt, DST, leap seconds |
441
600
  | `dateRange` | Range operations: overlap, gaps, merge |
442
601
  | `recurrence` | RRULE-inspired recurring patterns |
443
602
  | `cron` | Cron expression parsing and matching |
@@ -445,7 +604,7 @@ For complete API documentation, see the [Playground & Docs](https://ts-time-util
445
604
  | `compare` | Date sorting, grouping, statistics |
446
605
  | `iterate` | Date iteration and counting |
447
606
  | `naturalLanguage` | Natural language date parsing |
448
- | `holidays` | International holiday calculations |
607
+ | `holidays` | International holidays (20 countries) |
449
608
  | `locale` | Multi-language formatting (40+ locales) |
450
609
  | `workingHours` | Business hours calculations |
451
610
  | `serialize` | JSON date serialization |
@@ -455,6 +614,9 @@ For complete API documentation, see the [Playground & Docs](https://ts-time-util
455
614
  | `interval` | Time interval operations |
456
615
  | `rangePresets` | Common date range presets |
457
616
  | `parse` | Date parsing from various formats |
617
+ | `scheduling` | Appointment slots, availability, booking |
618
+ | `finance` | Market hours, trading days, settlement |
619
+ | `healthcare` | Medication schedules, shifts, on-call |
458
620
  | `plugins` | Plugin system for extensions |
459
621
  | `constants` | Time constants and types |
460
622
 
@@ -469,6 +631,24 @@ npm test # Run tests
469
631
  npm run lint # Lint code
470
632
  ```
471
633
 
634
+ ## Releasing
635
+
636
+ Releases are automated via GitHub Actions with npm trusted publishing (OIDC).
637
+
638
+ **To release a new version:**
639
+
640
+ ```bash
641
+ git tag v4.0.1 # Create tag (use semantic versioning)
642
+ git push --tags # Push tag → triggers publish workflow
643
+ ```
644
+
645
+ The workflow automatically:
646
+ 1. Sets `package.json` version from tag
647
+ 2. Runs lint, tests, and build
648
+ 3. Publishes to npm with provenance
649
+
650
+ **Version format:** Tags must match `v*` pattern (e.g., `v4.0.0`, `v4.1.0-beta.1`)
651
+
472
652
  ## License
473
653
 
474
654
  MIT
@@ -51,4 +51,29 @@ export declare function isBetween(date: Date, start: Date, end: Date, inclusive?
51
51
  * @param endDate - end date
52
52
  */
53
53
  export declare function businessDaysBetween(startDate: Date, endDate: Date): number;
54
+ /**
55
+ * Round a date to the nearest unit (rounds to closest)
56
+ * @param date - date to round
57
+ * @param unit - unit to round to
58
+ * @example
59
+ * roundToNearestUnit(new Date('2024-03-15T14:37:00'), 'hour') // 15:00
60
+ * roundToNearestUnit(new Date('2024-03-15T14:22:00'), 'hour') // 14:00
61
+ */
62
+ export declare function roundToNearestUnit(date: Date, unit: 'second' | 'minute' | 'hour' | 'day' | 'week' | 'month'): Date;
63
+ /**
64
+ * Round a date up (ceiling) to the specified unit
65
+ * @param date - date to round
66
+ * @param unit - unit to round to
67
+ * @example
68
+ * ceilDate(new Date('2024-03-15T14:01:00'), 'hour') // 15:00
69
+ */
70
+ export declare function ceilDate(date: Date, unit: 'second' | 'minute' | 'hour' | 'day' | 'week' | 'month'): Date;
71
+ /**
72
+ * Round a date down (floor) to the specified unit
73
+ * @param date - date to round
74
+ * @param unit - unit to round to
75
+ * @example
76
+ * floorDate(new Date('2024-03-15T14:59:00'), 'hour') // 14:00
77
+ */
78
+ export declare function floorDate(date: Date, unit: 'second' | 'minute' | 'hour' | 'day' | 'week' | 'month'): Date;
54
79
  //# sourceMappingURL=calculate.d.ts.map
@@ -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,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;;;;;;;;;;GAUG;AACH,wBAAgB,SAAS,CAAC,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,GAAG,EAAE,IAAI,EAAE,SAAS,GAAE,OAAc,GAAG,OAAO,CAShG;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;;;;;;;;;;GAUG;AACH,wBAAgB,SAAS,CAAC,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,GAAG,EAAE,IAAI,EAAE,SAAS,GAAE,OAAc,GAAG,OAAO,CAShG;AAED;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,SAAS,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,GAAG,MAAM,CAa1E;AAED;;;;;;;GAOG;AACH,wBAAgB,kBAAkB,CAChC,IAAI,EAAE,IAAI,EACV,IAAI,EAAE,QAAQ,GAAG,QAAQ,GAAG,MAAM,GAAG,KAAK,GAAG,MAAM,GAAG,OAAO,GAC5D,IAAI,CA2CN;AAED;;;;;;GAMG;AACH,wBAAgB,QAAQ,CACtB,IAAI,EAAE,IAAI,EACV,IAAI,EAAE,QAAQ,GAAG,QAAQ,GAAG,MAAM,GAAG,KAAK,GAAG,MAAM,GAAG,OAAO,GAC5D,IAAI,CA0BN;AAED;;;;;;GAMG;AACH,wBAAgB,SAAS,CACvB,IAAI,EAAE,IAAI,EACV,IAAI,EAAE,QAAQ,GAAG,QAAQ,GAAG,MAAM,GAAG,KAAK,GAAG,MAAM,GAAG,OAAO,GAC5D,IAAI,CA8BN"}
package/dist/calculate.js CHANGED
@@ -201,3 +201,128 @@ export function businessDaysBetween(startDate, endDate) {
201
201
  }
202
202
  return count;
203
203
  }
204
+ /**
205
+ * Round a date to the nearest unit (rounds to closest)
206
+ * @param date - date to round
207
+ * @param unit - unit to round to
208
+ * @example
209
+ * roundToNearestUnit(new Date('2024-03-15T14:37:00'), 'hour') // 15:00
210
+ * roundToNearestUnit(new Date('2024-03-15T14:22:00'), 'hour') // 14:00
211
+ */
212
+ export function roundToNearestUnit(date, unit) {
213
+ const d = new Date(date);
214
+ switch (unit) {
215
+ case 'second':
216
+ if (d.getMilliseconds() >= 500)
217
+ d.setSeconds(d.getSeconds() + 1);
218
+ d.setMilliseconds(0);
219
+ break;
220
+ case 'minute':
221
+ if (d.getSeconds() >= 30)
222
+ d.setMinutes(d.getMinutes() + 1);
223
+ d.setSeconds(0, 0);
224
+ break;
225
+ case 'hour':
226
+ if (d.getMinutes() >= 30)
227
+ d.setHours(d.getHours() + 1);
228
+ d.setMinutes(0, 0, 0);
229
+ break;
230
+ case 'day':
231
+ if (d.getHours() >= 12)
232
+ d.setDate(d.getDate() + 1);
233
+ d.setHours(0, 0, 0, 0);
234
+ break;
235
+ case 'week': {
236
+ const dayOfWeek = d.getDay();
237
+ const daysFromMonday = dayOfWeek === 0 ? 6 : dayOfWeek - 1;
238
+ if (daysFromMonday >= 4) {
239
+ d.setDate(d.getDate() + (7 - daysFromMonday));
240
+ }
241
+ else {
242
+ d.setDate(d.getDate() - daysFromMonday);
243
+ }
244
+ d.setHours(0, 0, 0, 0);
245
+ break;
246
+ }
247
+ case 'month': {
248
+ const daysInMonth = new Date(d.getFullYear(), d.getMonth() + 1, 0).getDate();
249
+ if (d.getDate() > daysInMonth / 2) {
250
+ d.setMonth(d.getMonth() + 1);
251
+ }
252
+ d.setDate(1);
253
+ d.setHours(0, 0, 0, 0);
254
+ break;
255
+ }
256
+ }
257
+ return d;
258
+ }
259
+ /**
260
+ * Round a date up (ceiling) to the specified unit
261
+ * @param date - date to round
262
+ * @param unit - unit to round to
263
+ * @example
264
+ * ceilDate(new Date('2024-03-15T14:01:00'), 'hour') // 15:00
265
+ */
266
+ export function ceilDate(date, unit) {
267
+ const floored = floorDate(date, unit);
268
+ if (floored.getTime() === date.getTime())
269
+ return new Date(date);
270
+ const d = new Date(floored);
271
+ switch (unit) {
272
+ case 'second':
273
+ d.setSeconds(d.getSeconds() + 1);
274
+ break;
275
+ case 'minute':
276
+ d.setMinutes(d.getMinutes() + 1);
277
+ break;
278
+ case 'hour':
279
+ d.setHours(d.getHours() + 1);
280
+ break;
281
+ case 'day':
282
+ d.setDate(d.getDate() + 1);
283
+ break;
284
+ case 'week':
285
+ d.setDate(d.getDate() + 7);
286
+ break;
287
+ case 'month':
288
+ d.setMonth(d.getMonth() + 1);
289
+ break;
290
+ }
291
+ return d;
292
+ }
293
+ /**
294
+ * Round a date down (floor) to the specified unit
295
+ * @param date - date to round
296
+ * @param unit - unit to round to
297
+ * @example
298
+ * floorDate(new Date('2024-03-15T14:59:00'), 'hour') // 14:00
299
+ */
300
+ export function floorDate(date, unit) {
301
+ const d = new Date(date);
302
+ switch (unit) {
303
+ case 'second':
304
+ d.setMilliseconds(0);
305
+ break;
306
+ case 'minute':
307
+ d.setSeconds(0, 0);
308
+ break;
309
+ case 'hour':
310
+ d.setMinutes(0, 0, 0);
311
+ break;
312
+ case 'day':
313
+ d.setHours(0, 0, 0, 0);
314
+ break;
315
+ case 'week': {
316
+ const dayOfWeek = d.getDay();
317
+ const daysFromMonday = dayOfWeek === 0 ? 6 : dayOfWeek - 1;
318
+ d.setDate(d.getDate() - daysFromMonday);
319
+ d.setHours(0, 0, 0, 0);
320
+ break;
321
+ }
322
+ case 'month':
323
+ d.setDate(1);
324
+ d.setHours(0, 0, 0, 0);
325
+ break;
326
+ }
327
+ return d;
328
+ }
@@ -182,4 +182,49 @@ export declare function getEndOfWeek(date: Date): Date;
182
182
  * @param month - month (0-11)
183
183
  */
184
184
  export declare function getWeeksInMonth(year: number, month: number): Date[][];
185
+ /**
186
+ * Get the ISO week-numbering year (year the week belongs to)
187
+ * @param date - date to get week year for
188
+ * @returns The year the ISO week belongs to (may differ from calendar year)
189
+ * @example
190
+ * getWeekYear(new Date('2024-01-01')) // 2024
191
+ * getWeekYear(new Date('2020-12-31')) // 2020 (belongs to week 53 of 2020)
192
+ */
193
+ export declare function getWeekYear(date: Date): number;
194
+ /**
195
+ * Get the last day of the decade containing the date
196
+ * @param date - any date
197
+ * @returns December 31 of the last year of the decade
198
+ * @example
199
+ * lastDayOfDecade(new Date('2024-06-15')) // 2029-12-31
200
+ */
201
+ export declare function lastDayOfDecade(date: Date): Date;
202
+ /**
203
+ * Get the first day of the decade containing the date
204
+ * @param date - any date
205
+ * @returns January 1 of the first year of the decade
206
+ * @example
207
+ * firstDayOfDecade(new Date('2024-06-15')) // 2020-01-01
208
+ */
209
+ export declare function firstDayOfDecade(date: Date): Date;
210
+ /**
211
+ * Get the last day of the century containing the date
212
+ * @param date - any date
213
+ * @returns December 31 of the last year of the century
214
+ * @example
215
+ * lastDayOfCentury(new Date('2024-06-15')) // 2099-12-31
216
+ */
217
+ export declare function lastDayOfCentury(date: Date): Date;
218
+ /**
219
+ * Get the start of a quarter
220
+ * @param date - any date
221
+ * @returns First day of the quarter
222
+ */
223
+ export declare function getStartOfQuarter(date: Date): Date;
224
+ /**
225
+ * Get the end of a quarter
226
+ * @param date - any date
227
+ * @returns Last day of the quarter
228
+ */
229
+ export declare function getEndOfQuarter(date: Date): Date;
185
230
  //# sourceMappingURL=calendar.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"calendar.d.ts","sourceRoot":"","sources":["../src/calendar.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"calendar.d.ts","sourceRoot":"","sources":["../src/calendar.ts"],"names":[],"mappings":"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"}
package/dist/calendar.js CHANGED
@@ -376,3 +376,71 @@ export function getWeeksInMonth(year, month) {
376
376
  }
377
377
  return weeks;
378
378
  }
379
+ /**
380
+ * Get the ISO week-numbering year (year the week belongs to)
381
+ * @param date - date to get week year for
382
+ * @returns The year the ISO week belongs to (may differ from calendar year)
383
+ * @example
384
+ * getWeekYear(new Date('2024-01-01')) // 2024
385
+ * getWeekYear(new Date('2020-12-31')) // 2020 (belongs to week 53 of 2020)
386
+ */
387
+ export function getWeekYear(date) {
388
+ const d = new Date(Date.UTC(date.getFullYear(), date.getMonth(), date.getDate()));
389
+ const dayNum = d.getUTCDay() || 7;
390
+ d.setUTCDate(d.getUTCDate() + 4 - dayNum);
391
+ return d.getUTCFullYear();
392
+ }
393
+ /**
394
+ * Get the last day of the decade containing the date
395
+ * @param date - any date
396
+ * @returns December 31 of the last year of the decade
397
+ * @example
398
+ * lastDayOfDecade(new Date('2024-06-15')) // 2029-12-31
399
+ */
400
+ export function lastDayOfDecade(date) {
401
+ const year = date.getFullYear();
402
+ const decadeEnd = Math.floor(year / 10) * 10 + 9;
403
+ return new Date(decadeEnd, 11, 31);
404
+ }
405
+ /**
406
+ * Get the first day of the decade containing the date
407
+ * @param date - any date
408
+ * @returns January 1 of the first year of the decade
409
+ * @example
410
+ * firstDayOfDecade(new Date('2024-06-15')) // 2020-01-01
411
+ */
412
+ export function firstDayOfDecade(date) {
413
+ const year = date.getFullYear();
414
+ const decadeStart = Math.floor(year / 10) * 10;
415
+ return new Date(decadeStart, 0, 1);
416
+ }
417
+ /**
418
+ * Get the last day of the century containing the date
419
+ * @param date - any date
420
+ * @returns December 31 of the last year of the century
421
+ * @example
422
+ * lastDayOfCentury(new Date('2024-06-15')) // 2099-12-31
423
+ */
424
+ export function lastDayOfCentury(date) {
425
+ const year = date.getFullYear();
426
+ const centuryEnd = Math.floor(year / 100) * 100 + 99;
427
+ return new Date(centuryEnd, 11, 31);
428
+ }
429
+ /**
430
+ * Get the start of a quarter
431
+ * @param date - any date
432
+ * @returns First day of the quarter
433
+ */
434
+ export function getStartOfQuarter(date) {
435
+ const month = Math.floor(date.getMonth() / 3) * 3;
436
+ return new Date(date.getFullYear(), month, 1);
437
+ }
438
+ /**
439
+ * Get the end of a quarter
440
+ * @param date - any date
441
+ * @returns Last day of the quarter
442
+ */
443
+ export function getEndOfQuarter(date) {
444
+ const month = Math.floor(date.getMonth() / 3) * 3 + 2;
445
+ return new Date(date.getFullYear(), month + 1, 0, 23, 59, 59, 999);
446
+ }