ts-time-utils 0.0.1

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 (80) hide show
  1. package/README.md +343 -0
  2. package/dist/age.d.ts +49 -0
  3. package/dist/age.d.ts.map +1 -0
  4. package/dist/age.js +106 -0
  5. package/dist/calculate.d.ts +49 -0
  6. package/dist/calculate.d.ts.map +1 -0
  7. package/dist/calculate.js +179 -0
  8. package/dist/calendar.d.ts +82 -0
  9. package/dist/calendar.d.ts.map +1 -0
  10. package/dist/calendar.js +154 -0
  11. package/dist/constants.d.ts +35 -0
  12. package/dist/constants.d.ts.map +1 -0
  13. package/dist/constants.js +17 -0
  14. package/dist/esm/age.d.ts +49 -0
  15. package/dist/esm/age.d.ts.map +1 -0
  16. package/dist/esm/age.js +106 -0
  17. package/dist/esm/calculate.d.ts +49 -0
  18. package/dist/esm/calculate.d.ts.map +1 -0
  19. package/dist/esm/calculate.js +179 -0
  20. package/dist/esm/calendar.d.ts +82 -0
  21. package/dist/esm/calendar.d.ts.map +1 -0
  22. package/dist/esm/calendar.js +154 -0
  23. package/dist/esm/constants.d.ts +35 -0
  24. package/dist/esm/constants.d.ts.map +1 -0
  25. package/dist/esm/constants.js +17 -0
  26. package/dist/esm/format.d.ts +25 -0
  27. package/dist/esm/format.d.ts.map +1 -0
  28. package/dist/esm/format.js +189 -0
  29. package/dist/esm/index.d.ts +17 -0
  30. package/dist/esm/index.d.ts.map +1 -0
  31. package/dist/esm/index.js +28 -0
  32. package/dist/esm/interval.d.ts +30 -0
  33. package/dist/esm/interval.d.ts.map +1 -0
  34. package/dist/esm/interval.js +86 -0
  35. package/dist/esm/parse.d.ts +31 -0
  36. package/dist/esm/parse.d.ts.map +1 -0
  37. package/dist/esm/parse.js +217 -0
  38. package/dist/esm/performance.d.ts +110 -0
  39. package/dist/esm/performance.d.ts.map +1 -0
  40. package/dist/esm/performance.js +222 -0
  41. package/dist/esm/rangePresets.d.ts +45 -0
  42. package/dist/esm/rangePresets.d.ts.map +1 -0
  43. package/dist/esm/rangePresets.js +124 -0
  44. package/dist/esm/timezone.d.ts +38 -0
  45. package/dist/esm/timezone.d.ts.map +1 -0
  46. package/dist/esm/timezone.js +99 -0
  47. package/dist/esm/validate.d.ts +62 -0
  48. package/dist/esm/validate.d.ts.map +1 -0
  49. package/dist/esm/validate.js +108 -0
  50. package/dist/esm/workingHours.d.ts +25 -0
  51. package/dist/esm/workingHours.d.ts.map +1 -0
  52. package/dist/esm/workingHours.js +107 -0
  53. package/dist/format.d.ts +25 -0
  54. package/dist/format.d.ts.map +1 -0
  55. package/dist/format.js +189 -0
  56. package/dist/index.d.ts +17 -0
  57. package/dist/index.d.ts.map +1 -0
  58. package/dist/index.js +28 -0
  59. package/dist/interval.d.ts +30 -0
  60. package/dist/interval.d.ts.map +1 -0
  61. package/dist/interval.js +86 -0
  62. package/dist/parse.d.ts +31 -0
  63. package/dist/parse.d.ts.map +1 -0
  64. package/dist/parse.js +217 -0
  65. package/dist/performance.d.ts +110 -0
  66. package/dist/performance.d.ts.map +1 -0
  67. package/dist/performance.js +222 -0
  68. package/dist/rangePresets.d.ts +45 -0
  69. package/dist/rangePresets.d.ts.map +1 -0
  70. package/dist/rangePresets.js +124 -0
  71. package/dist/timezone.d.ts +38 -0
  72. package/dist/timezone.d.ts.map +1 -0
  73. package/dist/timezone.js +99 -0
  74. package/dist/validate.d.ts +62 -0
  75. package/dist/validate.d.ts.map +1 -0
  76. package/dist/validate.js +108 -0
  77. package/dist/workingHours.d.ts +25 -0
  78. package/dist/workingHours.d.ts.map +1 -0
  79. package/dist/workingHours.js +107 -0
  80. package/package.json +102 -0
@@ -0,0 +1,189 @@
1
+ import { MILLISECONDS_PER_SECOND, MILLISECONDS_PER_MINUTE, MILLISECONDS_PER_HOUR, MILLISECONDS_PER_DAY, MILLISECONDS_PER_WEEK, MILLISECONDS_PER_MONTH, MILLISECONDS_PER_YEAR } from './constants.js';
2
+ /**
3
+ * Convert milliseconds to a human-readable duration.
4
+ * @param ms - milliseconds
5
+ * @param options - formatting options
6
+ */
7
+ export function formatDuration(ms, options = {}) {
8
+ const { includeMs = false, short = false, maxUnits = 2, round = false } = options;
9
+ if (ms < 0)
10
+ return '0' + (short ? 's' : ' seconds');
11
+ const mathFn = round ? Math.round : Math.floor;
12
+ const parts = [];
13
+ let remaining = ms;
14
+ // Years
15
+ if (remaining >= MILLISECONDS_PER_YEAR) {
16
+ const years = mathFn(remaining / MILLISECONDS_PER_YEAR);
17
+ parts.push(years + (short ? 'y' : ` year${years !== 1 ? 's' : ''}`));
18
+ remaining %= MILLISECONDS_PER_YEAR;
19
+ }
20
+ // Months
21
+ if (remaining >= MILLISECONDS_PER_MONTH && parts.length < maxUnits) {
22
+ const months = mathFn(remaining / MILLISECONDS_PER_MONTH);
23
+ parts.push(months + (short ? 'mo' : ` month${months !== 1 ? 's' : ''}`));
24
+ remaining %= MILLISECONDS_PER_MONTH;
25
+ }
26
+ // Weeks
27
+ if (remaining >= MILLISECONDS_PER_WEEK && parts.length < maxUnits) {
28
+ const weeks = mathFn(remaining / MILLISECONDS_PER_WEEK);
29
+ parts.push(weeks + (short ? 'w' : ` week${weeks !== 1 ? 's' : ''}`));
30
+ remaining %= MILLISECONDS_PER_WEEK;
31
+ }
32
+ // Days
33
+ if (remaining >= MILLISECONDS_PER_DAY && parts.length < maxUnits) {
34
+ const days = mathFn(remaining / MILLISECONDS_PER_DAY);
35
+ parts.push(days + (short ? 'd' : ` day${days !== 1 ? 's' : ''}`));
36
+ remaining %= MILLISECONDS_PER_DAY;
37
+ }
38
+ // Hours
39
+ if (remaining >= MILLISECONDS_PER_HOUR && parts.length < maxUnits) {
40
+ const hours = mathFn(remaining / MILLISECONDS_PER_HOUR);
41
+ parts.push(hours + (short ? 'h' : ` hour${hours !== 1 ? 's' : ''}`));
42
+ remaining %= MILLISECONDS_PER_HOUR;
43
+ }
44
+ // Minutes
45
+ if (remaining >= MILLISECONDS_PER_MINUTE && parts.length < maxUnits) {
46
+ const minutes = mathFn(remaining / MILLISECONDS_PER_MINUTE);
47
+ parts.push(minutes + (short ? 'm' : ` minute${minutes !== 1 ? 's' : ''}`));
48
+ remaining %= MILLISECONDS_PER_MINUTE;
49
+ }
50
+ // Seconds
51
+ if (remaining >= MILLISECONDS_PER_SECOND && parts.length < maxUnits) {
52
+ const seconds = mathFn(remaining / MILLISECONDS_PER_SECOND);
53
+ parts.push(seconds + (short ? 's' : ` second${seconds !== 1 ? 's' : ''}`));
54
+ remaining %= MILLISECONDS_PER_SECOND;
55
+ }
56
+ // Milliseconds
57
+ if ((remaining > 0 || parts.length === 0) && includeMs && parts.length < maxUnits) {
58
+ const milliseconds = mathFn(remaining);
59
+ parts.push(milliseconds + (short ? 'ms' : ` millisecond${milliseconds !== 1 ? 's' : ''}`));
60
+ }
61
+ if (parts.length === 0) {
62
+ return '0' + (short ? 's' : ' seconds');
63
+ }
64
+ return short ? parts.join(' ') : parts.join(', ');
65
+ }
66
+ /**
67
+ * Return a human-readable "time ago" string.
68
+ * @param date - past or future date
69
+ * @param options - formatting options
70
+ */
71
+ export function timeAgo(date, options = {}) {
72
+ const now = new Date();
73
+ const diffMs = now.getTime() - date.getTime();
74
+ const { short = false } = options;
75
+ const isFuture = diffMs < 0;
76
+ const absDiffMs = Math.abs(diffMs);
77
+ if (absDiffMs < MILLISECONDS_PER_MINUTE) {
78
+ const seconds = Math.floor(absDiffMs / MILLISECONDS_PER_SECOND);
79
+ const unit = short ? 's' : ` second${seconds !== 1 ? 's' : ''}`;
80
+ return isFuture ? `in ${seconds}${unit}` : `${seconds}${unit} ago`;
81
+ }
82
+ if (absDiffMs < MILLISECONDS_PER_HOUR) {
83
+ const minutes = Math.floor(absDiffMs / MILLISECONDS_PER_MINUTE);
84
+ const unit = short ? 'm' : ` minute${minutes !== 1 ? 's' : ''}`;
85
+ return isFuture ? `in ${minutes}${unit}` : `${minutes}${unit} ago`;
86
+ }
87
+ if (absDiffMs < MILLISECONDS_PER_DAY) {
88
+ const hours = Math.floor(absDiffMs / MILLISECONDS_PER_HOUR);
89
+ const unit = short ? 'h' : ` hour${hours !== 1 ? 's' : ''}`;
90
+ return isFuture ? `in ${hours}${unit}` : `${hours}${unit} ago`;
91
+ }
92
+ if (absDiffMs < MILLISECONDS_PER_WEEK) {
93
+ const days = Math.floor(absDiffMs / MILLISECONDS_PER_DAY);
94
+ const unit = short ? 'd' : ` day${days !== 1 ? 's' : ''}`;
95
+ return isFuture ? `in ${days}${unit}` : `${days}${unit} ago`;
96
+ }
97
+ if (absDiffMs < MILLISECONDS_PER_MONTH) {
98
+ const weeks = Math.floor(absDiffMs / MILLISECONDS_PER_WEEK);
99
+ const unit = short ? 'w' : ` week${weeks !== 1 ? 's' : ''}`;
100
+ return isFuture ? `in ${weeks}${unit}` : `${weeks}${unit} ago`;
101
+ }
102
+ if (absDiffMs < MILLISECONDS_PER_YEAR) {
103
+ const months = Math.floor(absDiffMs / MILLISECONDS_PER_MONTH);
104
+ const unit = short ? 'mo' : ` month${months !== 1 ? 's' : ''}`;
105
+ return isFuture ? `in ${months}${unit}` : `${months}${unit} ago`;
106
+ }
107
+ const years = Math.floor(absDiffMs / MILLISECONDS_PER_YEAR);
108
+ const unit = short ? 'y' : ` year${years !== 1 ? 's' : ''}`;
109
+ return isFuture ? `in ${years}${unit}` : `${years}${unit} ago`;
110
+ }
111
+ /**
112
+ * Format a date to a human-readable time string
113
+ * @param date - date to format
114
+ * @param format - format type
115
+ */
116
+ export function formatTime(date, format = '24h') {
117
+ switch (format) {
118
+ case '12h':
119
+ return date.toLocaleTimeString('en-US', {
120
+ hour: 'numeric',
121
+ minute: '2-digit',
122
+ hour12: true
123
+ });
124
+ case 'iso':
125
+ return date.toISOString();
126
+ case '24h':
127
+ default:
128
+ return date.toTimeString().split(' ')[0].slice(0, 5); // HH:MM
129
+ }
130
+ }
131
+ /**
132
+ * Parse a duration string like "1h 30m" into milliseconds
133
+ * @param duration - duration string (e.g., "1h 30m", "2d", "45s")
134
+ */
135
+ export function parseDuration(duration) {
136
+ const regex = /(\d+(?:\.\d+)?)\s*([a-zA-Z]+)/g;
137
+ let totalMs = 0;
138
+ let match;
139
+ while ((match = regex.exec(duration)) !== null) {
140
+ const value = parseFloat(match[1]);
141
+ const unit = match[2].toLowerCase();
142
+ switch (unit) {
143
+ case 'ms':
144
+ case 'millisecond':
145
+ case 'milliseconds':
146
+ totalMs += value;
147
+ break;
148
+ case 's':
149
+ case 'sec':
150
+ case 'second':
151
+ case 'seconds':
152
+ totalMs += value * MILLISECONDS_PER_SECOND;
153
+ break;
154
+ case 'm':
155
+ case 'min':
156
+ case 'minute':
157
+ case 'minutes':
158
+ totalMs += value * MILLISECONDS_PER_MINUTE;
159
+ break;
160
+ case 'h':
161
+ case 'hr':
162
+ case 'hour':
163
+ case 'hours':
164
+ totalMs += value * MILLISECONDS_PER_HOUR;
165
+ break;
166
+ case 'd':
167
+ case 'day':
168
+ case 'days':
169
+ totalMs += value * MILLISECONDS_PER_DAY;
170
+ break;
171
+ case 'w':
172
+ case 'week':
173
+ case 'weeks':
174
+ totalMs += value * MILLISECONDS_PER_WEEK;
175
+ break;
176
+ case 'mo':
177
+ case 'month':
178
+ case 'months':
179
+ totalMs += value * MILLISECONDS_PER_MONTH;
180
+ break;
181
+ case 'y':
182
+ case 'year':
183
+ case 'years':
184
+ totalMs += value * MILLISECONDS_PER_YEAR;
185
+ break;
186
+ }
187
+ }
188
+ return totalMs;
189
+ }
@@ -0,0 +1,17 @@
1
+ /**
2
+ * @fileoverview Main entry point for ts-time-utils library
3
+ * Re-exports all functions from individual modules for convenience
4
+ */
5
+ export { formatDuration, timeAgo, formatTime, parseDuration } from './format.js';
6
+ export { differenceInUnits, addTime, subtractTime, startOf, endOf, isBetween, businessDaysBetween } from './calculate.js';
7
+ export { isValidDate, isLeapYear, isPast, isFuture, isToday, isYesterday, isTomorrow, isSameDay, isWeekend, isWeekday, isValidTimeString, isValidISOString } from './validate.js';
8
+ export { calculateAge, getAgeInUnits, getLifeStage, getNextBirthday, getDaysUntilBirthday, isBirthday, type AgeResult } from './age.js';
9
+ export { getWeekNumber, getWeekOfMonth, getQuarter, getDayOfYear, getWeeksInYear, getDaysInMonth, getDaysInYear, getEaster, getMonthsInYear, getDaysInMonthArray, getWeekdaysInMonth, getFirstDayOfMonth, getLastDayOfMonth, getFirstDayOfYear, getLastDayOfYear } from './calendar.js';
10
+ export { parseDate, parseRelativeDate, parseTimeAgo, parseCustomFormat, parseManyFormats } from './parse.js';
11
+ export { sleep, timeout, debounce, throttle, retry, createStopwatch, measureTime, measureAsync, benchmark, Stopwatch, type BenchmarkResult } from './performance.js';
12
+ export { createInterval, isValidInterval, intervalDuration, intervalContains, intervalsOverlap, intervalIntersection, mergeIntervals, subtractInterval, splitIntervalByDay, totalIntervalCoverage, normalizeIntervals, type Interval } from './interval.js';
13
+ export { getTimezoneOffset, formatInTimeZone, getZonedTime, convertDateToZone, isValidTimeZone, COMMON_TIMEZONES, getLocalOffset, compareZoneOffsets, reinterpretAsZone, type ZonedTime } from './timezone.js';
14
+ export { DEFAULT_WORKING_HOURS, isWorkingDay, isWorkingTime, nextWorkingTime, workingTimeBetween, addWorkingHours, type WorkingHoursConfig } from './workingHours.js';
15
+ export { today, yesterday, tomorrow, lastNDays, nextNDays, thisWeek, lastWeek, nextWeek, thisMonth, lastMonth, nextMonth, thisYear, lastYear, nextYear, rollingWindowDays, quarterRange, lastQuarter, nextQuarter, RANGE_PRESETS, type DateRange } from './rangePresets.js';
16
+ export { MILLISECONDS_PER_SECOND, MILLISECONDS_PER_MINUTE, MILLISECONDS_PER_HOUR, MILLISECONDS_PER_DAY, MILLISECONDS_PER_WEEK, MILLISECONDS_PER_MONTH, MILLISECONDS_PER_YEAR, SECONDS_PER_MINUTE, SECONDS_PER_HOUR, SECONDS_PER_DAY, SECONDS_PER_WEEK, type TimeUnit, type FormatOptions } from './constants.js';
17
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,EACL,cAAc,EACd,OAAO,EACP,UAAU,EACV,aAAa,EACd,MAAM,aAAa,CAAC;AAGrB,OAAO,EACL,iBAAiB,EACjB,OAAO,EACP,YAAY,EACZ,OAAO,EACP,KAAK,EACL,SAAS,EACT,mBAAmB,EACpB,MAAM,gBAAgB,CAAC;AAGxB,OAAO,EACL,WAAW,EACX,UAAU,EACV,MAAM,EACN,QAAQ,EACR,OAAO,EACP,WAAW,EACX,UAAU,EACV,SAAS,EACT,SAAS,EACT,SAAS,EACT,iBAAiB,EACjB,gBAAgB,EACjB,MAAM,eAAe,CAAC;AAGvB,OAAO,EACL,YAAY,EACZ,aAAa,EACb,YAAY,EACZ,eAAe,EACf,oBAAoB,EACpB,UAAU,EACV,KAAK,SAAS,EACf,MAAM,UAAU,CAAC;AAGlB,OAAO,EACL,aAAa,EACb,cAAc,EACd,UAAU,EACV,YAAY,EACZ,cAAc,EACd,cAAc,EACd,aAAa,EACb,SAAS,EACT,eAAe,EACf,mBAAmB,EACnB,kBAAkB,EAClB,kBAAkB,EAClB,iBAAiB,EACjB,iBAAiB,EACjB,gBAAgB,EACjB,MAAM,eAAe,CAAC;AAGvB,OAAO,EACL,SAAS,EACT,iBAAiB,EACjB,YAAY,EACZ,iBAAiB,EACjB,gBAAgB,EACjB,MAAM,YAAY,CAAC;AAGpB,OAAO,EACL,KAAK,EACL,OAAO,EACP,QAAQ,EACR,QAAQ,EACR,KAAK,EACL,eAAe,EACf,WAAW,EACX,YAAY,EACZ,SAAS,EACT,SAAS,EACT,KAAK,eAAe,EACrB,MAAM,kBAAkB,CAAC;AAG1B,OAAO,EACL,cAAc,EACd,eAAe,EACf,gBAAgB,EAChB,gBAAgB,EAChB,gBAAgB,EAChB,oBAAoB,EACpB,cAAc,EACd,gBAAgB,EAChB,kBAAkB,EAClB,qBAAqB,EACrB,kBAAkB,EAClB,KAAK,QAAQ,EACd,MAAM,eAAe,CAAC;AAGvB,OAAO,EACL,iBAAiB,EACjB,gBAAgB,EAChB,YAAY,EACZ,iBAAiB,EACjB,eAAe,EACf,gBAAgB,EAChB,cAAc,EACd,kBAAkB,EAClB,iBAAiB,EACjB,KAAK,SAAS,EACf,MAAM,eAAe,CAAC;AAGvB,OAAO,EACL,qBAAqB,EACrB,YAAY,EACZ,aAAa,EACb,eAAe,EACf,kBAAkB,EAClB,eAAe,EACf,KAAK,kBAAkB,EACxB,MAAM,mBAAmB,CAAC;AAG3B,OAAO,EACL,KAAK,EACL,SAAS,EACT,QAAQ,EACR,SAAS,EACT,SAAS,EACT,QAAQ,EACR,QAAQ,EACR,QAAQ,EACR,SAAS,EACT,SAAS,EACT,SAAS,EACT,QAAQ,EACR,QAAQ,EACR,QAAQ,EACR,iBAAiB,EACjB,YAAY,EACZ,WAAW,EACX,WAAW,EACX,aAAa,EACb,KAAK,SAAS,EACf,MAAM,mBAAmB,CAAC;AAG3B,OAAO,EACL,uBAAuB,EACvB,uBAAuB,EACvB,qBAAqB,EACrB,oBAAoB,EACpB,qBAAqB,EACrB,sBAAsB,EACtB,qBAAqB,EACrB,kBAAkB,EAClB,gBAAgB,EAChB,eAAe,EACf,gBAAgB,EAChB,KAAK,QAAQ,EACb,KAAK,aAAa,EACnB,MAAM,gBAAgB,CAAC"}
@@ -0,0 +1,28 @@
1
+ /**
2
+ * @fileoverview Main entry point for ts-time-utils library
3
+ * Re-exports all functions from individual modules for convenience
4
+ */
5
+ // Format utilities
6
+ export { formatDuration, timeAgo, formatTime, parseDuration } from './format.js';
7
+ // Calculation utilities
8
+ export { differenceInUnits, addTime, subtractTime, startOf, endOf, isBetween, businessDaysBetween } from './calculate.js';
9
+ // Validation utilities
10
+ export { isValidDate, isLeapYear, isPast, isFuture, isToday, isYesterday, isTomorrow, isSameDay, isWeekend, isWeekday, isValidTimeString, isValidISOString } from './validate.js';
11
+ // Age utilities
12
+ export { calculateAge, getAgeInUnits, getLifeStage, getNextBirthday, getDaysUntilBirthday, isBirthday } from './age.js';
13
+ // Calendar utilities
14
+ export { getWeekNumber, getWeekOfMonth, getQuarter, getDayOfYear, getWeeksInYear, getDaysInMonth, getDaysInYear, getEaster, getMonthsInYear, getDaysInMonthArray, getWeekdaysInMonth, getFirstDayOfMonth, getLastDayOfMonth, getFirstDayOfYear, getLastDayOfYear } from './calendar.js';
15
+ // Parse utilities
16
+ export { parseDate, parseRelativeDate, parseTimeAgo, parseCustomFormat, parseManyFormats } from './parse.js';
17
+ // Performance utilities
18
+ export { sleep, timeout, debounce, throttle, retry, createStopwatch, measureTime, measureAsync, benchmark, Stopwatch } from './performance.js';
19
+ // Interval utilities
20
+ export { createInterval, isValidInterval, intervalDuration, intervalContains, intervalsOverlap, intervalIntersection, mergeIntervals, subtractInterval, splitIntervalByDay, totalIntervalCoverage, normalizeIntervals } from './interval.js';
21
+ // Timezone utilities
22
+ export { getTimezoneOffset, formatInTimeZone, getZonedTime, convertDateToZone, isValidTimeZone, COMMON_TIMEZONES, getLocalOffset, compareZoneOffsets, reinterpretAsZone } from './timezone.js';
23
+ // Working hours utilities
24
+ export { DEFAULT_WORKING_HOURS, isWorkingDay, isWorkingTime, nextWorkingTime, workingTimeBetween, addWorkingHours } from './workingHours.js';
25
+ // Range preset utilities
26
+ export { today, yesterday, tomorrow, lastNDays, nextNDays, thisWeek, lastWeek, nextWeek, thisMonth, lastMonth, nextMonth, thisYear, lastYear, nextYear, rollingWindowDays, quarterRange, lastQuarter, nextQuarter, RANGE_PRESETS } from './rangePresets.js';
27
+ // Constants and types
28
+ export { MILLISECONDS_PER_SECOND, MILLISECONDS_PER_MINUTE, MILLISECONDS_PER_HOUR, MILLISECONDS_PER_DAY, MILLISECONDS_PER_WEEK, MILLISECONDS_PER_MONTH, MILLISECONDS_PER_YEAR, SECONDS_PER_MINUTE, SECONDS_PER_HOUR, SECONDS_PER_DAY, SECONDS_PER_WEEK } from './constants.js';
@@ -0,0 +1,30 @@
1
+ /**
2
+ * Interval utilities: operations on time intervals [start, end)
3
+ */
4
+ export interface Interval {
5
+ start: Date;
6
+ end: Date;
7
+ }
8
+ /** Create an interval ensuring start <= end */
9
+ export declare function createInterval(start: Date | string | number, end: Date | string | number): Interval | null;
10
+ /** Validate an object is a proper interval */
11
+ export declare function isValidInterval(i: any): i is Interval;
12
+ /** Duration of interval in ms */
13
+ export declare function intervalDuration(i: Interval): number;
14
+ /** Whether interval contains date (inclusive start, exclusive end) */
15
+ export declare function intervalContains(i: Interval, date: Date | number): boolean;
16
+ /** Whether two intervals overlap */
17
+ export declare function intervalsOverlap(a: Interval, b: Interval): boolean;
18
+ /** Intersection of two intervals, or null */
19
+ export declare function intervalIntersection(a: Interval, b: Interval): Interval | null;
20
+ /** Merge overlapping or touching intervals into a minimal set */
21
+ export declare function mergeIntervals(intervals: Interval[]): Interval[];
22
+ /** Subtract interval b from a (can split into 0,1,2 intervals) */
23
+ export declare function subtractInterval(a: Interval, b: Interval): Interval[];
24
+ /** Split an interval into day-boundary intervals (UTC based) */
25
+ export declare function splitIntervalByDay(i: Interval): Interval[];
26
+ /** Total covered duration of possibly overlapping intervals */
27
+ export declare function totalIntervalCoverage(intervals: Interval[]): number;
28
+ /** Normalize array: filter invalid and merge */
29
+ export declare function normalizeIntervals(intervals: (Interval | null | undefined)[]): Interval[];
30
+ //# sourceMappingURL=interval.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"interval.d.ts","sourceRoot":"","sources":["../../src/interval.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,MAAM,WAAW,QAAQ;IACvB,KAAK,EAAE,IAAI,CAAC;IACZ,GAAG,EAAE,IAAI,CAAC;CACX;AAED,+CAA+C;AAC/C,wBAAgB,cAAc,CAAC,KAAK,EAAE,IAAI,GAAG,MAAM,GAAG,MAAM,EAAE,GAAG,EAAE,IAAI,GAAG,MAAM,GAAG,MAAM,GAAG,QAAQ,GAAG,IAAI,CAK1G;AAED,8CAA8C;AAC9C,wBAAgB,eAAe,CAAC,CAAC,EAAE,GAAG,GAAG,CAAC,IAAI,QAAQ,CAErD;AAED,iCAAiC;AACjC,wBAAgB,gBAAgB,CAAC,CAAC,EAAE,QAAQ,GAAG,MAAM,CAEpD;AAED,sEAAsE;AACtE,wBAAgB,gBAAgB,CAAC,CAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,IAAI,GAAG,MAAM,GAAG,OAAO,CAG1E;AAED,oCAAoC;AACpC,wBAAgB,gBAAgB,CAAC,CAAC,EAAE,QAAQ,EAAE,CAAC,EAAE,QAAQ,GAAG,OAAO,CAElE;AAED,6CAA6C;AAC7C,wBAAgB,oBAAoB,CAAC,CAAC,EAAE,QAAQ,EAAE,CAAC,EAAE,QAAQ,GAAG,QAAQ,GAAG,IAAI,CAI9E;AAED,iEAAiE;AACjE,wBAAgB,cAAc,CAAC,SAAS,EAAE,QAAQ,EAAE,GAAG,QAAQ,EAAE,CAgBhE;AAED,kEAAkE;AAClE,wBAAgB,gBAAgB,CAAC,CAAC,EAAE,QAAQ,EAAE,CAAC,EAAE,QAAQ,GAAG,QAAQ,EAAE,CAMrE;AAED,gEAAgE;AAChE,wBAAgB,kBAAkB,CAAC,CAAC,EAAE,QAAQ,GAAG,QAAQ,EAAE,CAU1D;AAED,+DAA+D;AAC/D,wBAAgB,qBAAqB,CAAC,SAAS,EAAE,QAAQ,EAAE,GAAG,MAAM,CAEnE;AAED,gDAAgD;AAChD,wBAAgB,kBAAkB,CAAC,SAAS,EAAE,CAAC,QAAQ,GAAG,IAAI,GAAG,SAAS,CAAC,EAAE,GAAG,QAAQ,EAAE,CAEzF"}
@@ -0,0 +1,86 @@
1
+ /**
2
+ * Interval utilities: operations on time intervals [start, end)
3
+ */
4
+ /** Create an interval ensuring start <= end */
5
+ export function createInterval(start, end) {
6
+ const s = new Date(start);
7
+ const e = new Date(end);
8
+ if (isNaN(s.getTime()) || isNaN(e.getTime()) || s > e)
9
+ return null;
10
+ return { start: s, end: e };
11
+ }
12
+ /** Validate an object is a proper interval */
13
+ export function isValidInterval(i) {
14
+ return !!i && i.start instanceof Date && i.end instanceof Date && !isNaN(i.start) && !isNaN(i.end) && i.start <= i.end;
15
+ }
16
+ /** Duration of interval in ms */
17
+ export function intervalDuration(i) {
18
+ return i.end.getTime() - i.start.getTime();
19
+ }
20
+ /** Whether interval contains date (inclusive start, exclusive end) */
21
+ export function intervalContains(i, date) {
22
+ const d = date instanceof Date ? date : new Date(date);
23
+ return d >= i.start && d < i.end;
24
+ }
25
+ /** Whether two intervals overlap */
26
+ export function intervalsOverlap(a, b) {
27
+ return a.start < b.end && b.start < a.end;
28
+ }
29
+ /** Intersection of two intervals, or null */
30
+ export function intervalIntersection(a, b) {
31
+ const start = a.start > b.start ? a.start : b.start;
32
+ const end = a.end < b.end ? a.end : b.end;
33
+ return start < end ? { start, end } : null;
34
+ }
35
+ /** Merge overlapping or touching intervals into a minimal set */
36
+ export function mergeIntervals(intervals) {
37
+ if (intervals.length === 0)
38
+ return [];
39
+ const sorted = [...intervals].sort((a, b) => a.start.getTime() - b.start.getTime());
40
+ const result = [];
41
+ let current = { ...sorted[0] };
42
+ for (let i = 1; i < sorted.length; i++) {
43
+ const next = sorted[i];
44
+ if (next.start <= current.end) { // overlap or touching
45
+ if (next.end > current.end)
46
+ current.end = next.end;
47
+ }
48
+ else {
49
+ result.push(current);
50
+ current = { ...next };
51
+ }
52
+ }
53
+ result.push(current);
54
+ return result;
55
+ }
56
+ /** Subtract interval b from a (can split into 0,1,2 intervals) */
57
+ export function subtractInterval(a, b) {
58
+ if (!intervalsOverlap(a, b))
59
+ return [a];
60
+ const parts = [];
61
+ if (b.start > a.start)
62
+ parts.push({ start: a.start, end: b.start });
63
+ if (b.end < a.end)
64
+ parts.push({ start: b.end, end: a.end });
65
+ return parts;
66
+ }
67
+ /** Split an interval into day-boundary intervals (UTC based) */
68
+ export function splitIntervalByDay(i) {
69
+ const res = [];
70
+ let cursor = new Date(i.start);
71
+ while (cursor < i.end) {
72
+ const dayEnd = new Date(Date.UTC(cursor.getUTCFullYear(), cursor.getUTCMonth(), cursor.getUTCDate() + 1));
73
+ const end = dayEnd < i.end ? dayEnd : i.end;
74
+ res.push({ start: new Date(cursor), end: new Date(end) });
75
+ cursor = end;
76
+ }
77
+ return res;
78
+ }
79
+ /** Total covered duration of possibly overlapping intervals */
80
+ export function totalIntervalCoverage(intervals) {
81
+ return mergeIntervals(intervals).reduce((sum, i) => sum + intervalDuration(i), 0);
82
+ }
83
+ /** Normalize array: filter invalid and merge */
84
+ export function normalizeIntervals(intervals) {
85
+ return mergeIntervals(intervals.filter(isValidInterval));
86
+ }
@@ -0,0 +1,31 @@
1
+ /**
2
+ * Advanced date parsing utilities
3
+ */
4
+ /**
5
+ * Parse various date formats intelligently
6
+ * @param input - date string, number, or Date object
7
+ */
8
+ export declare function parseDate(input: string | number | Date): Date | null;
9
+ /**
10
+ * Parse relative date strings like "tomorrow", "next monday", "2 weeks ago"
11
+ * @param input - relative date string
12
+ */
13
+ export declare function parseRelativeDate(input: string): Date | null;
14
+ /**
15
+ * Parse "time ago" strings like "5 minutes ago", "2 hours ago"
16
+ * @param input - time ago string
17
+ */
18
+ export declare function parseTimeAgo(input: string): Date | null;
19
+ /**
20
+ * Parse custom date format
21
+ * @param dateString - date string to parse
22
+ * @param format - format pattern (e.g., "YYYY-MM-DD", "DD/MM/YYYY")
23
+ */
24
+ export declare function parseCustomFormat(dateString: string, format: string): Date | null;
25
+ /**
26
+ * Try parsing with multiple formats
27
+ * @param dateString - date string to parse
28
+ * @param formats - array of format patterns to try
29
+ */
30
+ export declare function parseManyFormats(dateString: string, formats: string[]): Date | null;
31
+ //# sourceMappingURL=parse.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"parse.d.ts","sourceRoot":"","sources":["../../src/parse.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH;;;GAGG;AACH,wBAAgB,SAAS,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,CAuDpE;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI,CA6C5D;AAED;;;GAGG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI,CAiBvD;AAED;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,UAAU,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI,CAmDjF;AAED;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,UAAU,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,GAAG,IAAI,GAAG,IAAI,CAMnF"}
@@ -0,0 +1,217 @@
1
+ /**
2
+ * Advanced date parsing utilities
3
+ */
4
+ /**
5
+ * Parse various date formats intelligently
6
+ * @param input - date string, number, or Date object
7
+ */
8
+ export function parseDate(input) {
9
+ if (input instanceof Date) {
10
+ return isNaN(input.getTime()) ? null : input;
11
+ }
12
+ if (typeof input === 'number') {
13
+ const date = new Date(input);
14
+ return isNaN(date.getTime()) ? null : date;
15
+ }
16
+ if (typeof input !== 'string') {
17
+ return null;
18
+ }
19
+ // Try native Date parsing first, but validate the result
20
+ const nativeDate = new Date(input);
21
+ if (!isNaN(nativeDate.getTime())) {
22
+ // Additional validation for edge cases like "2025-02-30"
23
+ if (input.includes('-') && input.match(/^\d{4}-\d{2}-\d{2}/)) {
24
+ const [year, month, day] = input.split('-').map(Number);
25
+ const testDate = new Date(year, month - 1, day);
26
+ if (testDate.getFullYear() !== year || testDate.getMonth() !== month - 1 || testDate.getDate() !== day) {
27
+ return null;
28
+ }
29
+ }
30
+ return nativeDate;
31
+ }
32
+ // Try common patterns
33
+ const patterns = [
34
+ /^(\d{4})-(\d{2})-(\d{2})$/, // YYYY-MM-DD
35
+ /^(\d{2})\/(\d{2})\/(\d{4})$/, // MM/DD/YYYY
36
+ /^(\d{2})-(\d{2})-(\d{4})$/, // MM-DD-YYYY
37
+ /^(\d{4})(\d{2})(\d{2})$/, // YYYYMMDD
38
+ ];
39
+ for (const pattern of patterns) {
40
+ const match = input.match(pattern);
41
+ if (match) {
42
+ const [, first, second, third] = match;
43
+ // Try different interpretations based on pattern
44
+ if (pattern.source.includes('(\\d{4})')) {
45
+ // Year first format
46
+ const date = new Date(parseInt(first), parseInt(second) - 1, parseInt(third));
47
+ if (!isNaN(date.getTime()))
48
+ return date;
49
+ }
50
+ else {
51
+ // Month/day first format (assuming US format)
52
+ const date = new Date(parseInt(third), parseInt(first) - 1, parseInt(second));
53
+ if (!isNaN(date.getTime()))
54
+ return date;
55
+ }
56
+ }
57
+ }
58
+ return null;
59
+ }
60
+ /**
61
+ * Parse relative date strings like "tomorrow", "next monday", "2 weeks ago"
62
+ * @param input - relative date string
63
+ */
64
+ export function parseRelativeDate(input) {
65
+ const now = new Date();
66
+ const lowercaseInput = input.toLowerCase().trim();
67
+ // Handle simple cases
68
+ switch (lowercaseInput) {
69
+ case 'now':
70
+ case 'today':
71
+ return new Date(now.getFullYear(), now.getMonth(), now.getDate());
72
+ case 'yesterday':
73
+ return new Date(now.getFullYear(), now.getMonth(), now.getDate() - 1);
74
+ case 'tomorrow':
75
+ return new Date(now.getFullYear(), now.getMonth(), now.getDate() + 1);
76
+ }
77
+ // Handle "X time ago" or "in X time"
78
+ const agoMatch = lowercaseInput.match(/^(\d+)\s+(second|minute|hour|day|week|month|year)s?\s+ago$/);
79
+ if (agoMatch) {
80
+ const [, amount, unit] = agoMatch;
81
+ return subtractTimeUnits(now, parseInt(amount), unit);
82
+ }
83
+ const inMatch = lowercaseInput.match(/^in\s+(\d+)\s+(second|minute|hour|day|week|month|year)s?$/);
84
+ if (inMatch) {
85
+ const [, amount, unit] = inMatch;
86
+ return addTimeUnits(now, parseInt(amount), unit);
87
+ }
88
+ // Handle "next/last weekday"
89
+ const weekdays = ['sunday', 'monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'saturday'];
90
+ const nextWeekdayMatch = lowercaseInput.match(/^next\s+(sunday|monday|tuesday|wednesday|thursday|friday|saturday)$/);
91
+ if (nextWeekdayMatch) {
92
+ const targetDay = weekdays.indexOf(nextWeekdayMatch[1]);
93
+ const daysUntilTarget = (targetDay - now.getDay() + 7) % 7 || 7;
94
+ return new Date(now.getFullYear(), now.getMonth(), now.getDate() + daysUntilTarget);
95
+ }
96
+ const lastWeekdayMatch = lowercaseInput.match(/^last\s+(sunday|monday|tuesday|wednesday|thursday|friday|saturday)$/);
97
+ if (lastWeekdayMatch) {
98
+ const targetDay = weekdays.indexOf(lastWeekdayMatch[1]);
99
+ const daysSinceTarget = (now.getDay() - targetDay + 7) % 7 || 7;
100
+ return new Date(now.getFullYear(), now.getMonth(), now.getDate() - daysSinceTarget);
101
+ }
102
+ return null;
103
+ }
104
+ /**
105
+ * Parse "time ago" strings like "5 minutes ago", "2 hours ago"
106
+ * @param input - time ago string
107
+ */
108
+ export function parseTimeAgo(input) {
109
+ const now = new Date();
110
+ const lowercaseInput = input.toLowerCase().trim();
111
+ // Handle simple cases
112
+ if (lowercaseInput === 'just now' || lowercaseInput === 'now') {
113
+ return now;
114
+ }
115
+ // Handle "X time ago"
116
+ const match = lowercaseInput.match(/^(\d+)\s+(second|minute|hour|day|week|month|year)s?\s+ago$/);
117
+ if (match) {
118
+ const [, amount, unit] = match;
119
+ return subtractTimeUnits(now, parseInt(amount), unit);
120
+ }
121
+ return null;
122
+ }
123
+ /**
124
+ * Parse custom date format
125
+ * @param dateString - date string to parse
126
+ * @param format - format pattern (e.g., "YYYY-MM-DD", "DD/MM/YYYY")
127
+ */
128
+ export function parseCustomFormat(dateString, format) {
129
+ // Simple implementation for common patterns
130
+ const formatMap = {
131
+ 'YYYY-MM-DD': /^(\d{4})-(\d{2})-(\d{2})$/,
132
+ 'DD/MM/YYYY': /^(\d{2})\/(\d{2})\/(\d{4})$/,
133
+ 'MM/DD/YYYY': /^(\d{2})\/(\d{2})\/(\d{4})$/,
134
+ 'DD-MM-YYYY': /^(\d{2})-(\d{2})-(\d{4})$/,
135
+ 'MM-DD-YYYY': /^(\d{2})-(\d{2})-(\d{4})$/,
136
+ };
137
+ const regex = formatMap[format];
138
+ if (!regex)
139
+ return null;
140
+ const match = dateString.match(regex);
141
+ if (!match)
142
+ return null;
143
+ const [, first, second, third] = match;
144
+ let year, month, day;
145
+ switch (format) {
146
+ case 'YYYY-MM-DD':
147
+ year = parseInt(first);
148
+ month = parseInt(second) - 1;
149
+ day = parseInt(third);
150
+ break;
151
+ case 'DD/MM/YYYY':
152
+ case 'DD-MM-YYYY':
153
+ day = parseInt(first);
154
+ month = parseInt(second) - 1;
155
+ year = parseInt(third);
156
+ break;
157
+ case 'MM/DD/YYYY':
158
+ case 'MM-DD-YYYY':
159
+ month = parseInt(first) - 1;
160
+ day = parseInt(second);
161
+ year = parseInt(third);
162
+ break;
163
+ default:
164
+ return null;
165
+ }
166
+ const date = new Date(year, month, day);
167
+ // Validate that the date components match what was parsed
168
+ // This catches cases like February 30th which JS converts to March 2nd
169
+ if (date.getFullYear() !== year || date.getMonth() !== month || date.getDate() !== day) {
170
+ return null;
171
+ }
172
+ return isNaN(date.getTime()) ? null : date;
173
+ }
174
+ /**
175
+ * Try parsing with multiple formats
176
+ * @param dateString - date string to parse
177
+ * @param formats - array of format patterns to try
178
+ */
179
+ export function parseManyFormats(dateString, formats) {
180
+ for (const format of formats) {
181
+ const result = parseCustomFormat(dateString, format);
182
+ if (result)
183
+ return result;
184
+ }
185
+ return null;
186
+ }
187
+ // Helper functions
188
+ function addTimeUnits(date, amount, unit) {
189
+ const result = new Date(date);
190
+ switch (unit) {
191
+ case 'second':
192
+ result.setSeconds(result.getSeconds() + amount);
193
+ break;
194
+ case 'minute':
195
+ result.setMinutes(result.getMinutes() + amount);
196
+ break;
197
+ case 'hour':
198
+ result.setHours(result.getHours() + amount);
199
+ break;
200
+ case 'day':
201
+ result.setDate(result.getDate() + amount);
202
+ break;
203
+ case 'week':
204
+ result.setDate(result.getDate() + (amount * 7));
205
+ break;
206
+ case 'month':
207
+ result.setMonth(result.getMonth() + amount);
208
+ break;
209
+ case 'year':
210
+ result.setFullYear(result.getFullYear() + amount);
211
+ break;
212
+ }
213
+ return result;
214
+ }
215
+ function subtractTimeUnits(date, amount, unit) {
216
+ return addTimeUnits(date, -amount, unit);
217
+ }