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.
- package/README.md +343 -0
- package/dist/age.d.ts +49 -0
- package/dist/age.d.ts.map +1 -0
- package/dist/age.js +106 -0
- package/dist/calculate.d.ts +49 -0
- package/dist/calculate.d.ts.map +1 -0
- package/dist/calculate.js +179 -0
- package/dist/calendar.d.ts +82 -0
- package/dist/calendar.d.ts.map +1 -0
- package/dist/calendar.js +154 -0
- package/dist/constants.d.ts +35 -0
- package/dist/constants.d.ts.map +1 -0
- package/dist/constants.js +17 -0
- package/dist/esm/age.d.ts +49 -0
- package/dist/esm/age.d.ts.map +1 -0
- package/dist/esm/age.js +106 -0
- package/dist/esm/calculate.d.ts +49 -0
- package/dist/esm/calculate.d.ts.map +1 -0
- package/dist/esm/calculate.js +179 -0
- package/dist/esm/calendar.d.ts +82 -0
- package/dist/esm/calendar.d.ts.map +1 -0
- package/dist/esm/calendar.js +154 -0
- package/dist/esm/constants.d.ts +35 -0
- package/dist/esm/constants.d.ts.map +1 -0
- package/dist/esm/constants.js +17 -0
- package/dist/esm/format.d.ts +25 -0
- package/dist/esm/format.d.ts.map +1 -0
- package/dist/esm/format.js +189 -0
- package/dist/esm/index.d.ts +17 -0
- package/dist/esm/index.d.ts.map +1 -0
- package/dist/esm/index.js +28 -0
- package/dist/esm/interval.d.ts +30 -0
- package/dist/esm/interval.d.ts.map +1 -0
- package/dist/esm/interval.js +86 -0
- package/dist/esm/parse.d.ts +31 -0
- package/dist/esm/parse.d.ts.map +1 -0
- package/dist/esm/parse.js +217 -0
- package/dist/esm/performance.d.ts +110 -0
- package/dist/esm/performance.d.ts.map +1 -0
- package/dist/esm/performance.js +222 -0
- package/dist/esm/rangePresets.d.ts +45 -0
- package/dist/esm/rangePresets.d.ts.map +1 -0
- package/dist/esm/rangePresets.js +124 -0
- package/dist/esm/timezone.d.ts +38 -0
- package/dist/esm/timezone.d.ts.map +1 -0
- package/dist/esm/timezone.js +99 -0
- package/dist/esm/validate.d.ts +62 -0
- package/dist/esm/validate.d.ts.map +1 -0
- package/dist/esm/validate.js +108 -0
- package/dist/esm/workingHours.d.ts +25 -0
- package/dist/esm/workingHours.d.ts.map +1 -0
- package/dist/esm/workingHours.js +107 -0
- package/dist/format.d.ts +25 -0
- package/dist/format.d.ts.map +1 -0
- package/dist/format.js +189 -0
- package/dist/index.d.ts +17 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +28 -0
- package/dist/interval.d.ts +30 -0
- package/dist/interval.d.ts.map +1 -0
- package/dist/interval.js +86 -0
- package/dist/parse.d.ts +31 -0
- package/dist/parse.d.ts.map +1 -0
- package/dist/parse.js +217 -0
- package/dist/performance.d.ts +110 -0
- package/dist/performance.d.ts.map +1 -0
- package/dist/performance.js +222 -0
- package/dist/rangePresets.d.ts +45 -0
- package/dist/rangePresets.d.ts.map +1 -0
- package/dist/rangePresets.js +124 -0
- package/dist/timezone.d.ts +38 -0
- package/dist/timezone.d.ts.map +1 -0
- package/dist/timezone.js +99 -0
- package/dist/validate.d.ts +62 -0
- package/dist/validate.d.ts.map +1 -0
- package/dist/validate.js +108 -0
- package/dist/workingHours.d.ts +25 -0
- package/dist/workingHours.d.ts.map +1 -0
- package/dist/workingHours.js +107 -0
- package/package.json +102 -0
package/dist/interval.js
ADDED
|
@@ -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
|
+
}
|
package/dist/parse.d.ts
ADDED
|
@@ -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"}
|
package/dist/parse.js
ADDED
|
@@ -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
|
+
}
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Async time utilities for delays, timeouts, and performance
|
|
3
|
+
*/
|
|
4
|
+
/**
|
|
5
|
+
* Sleep for a specified number of milliseconds
|
|
6
|
+
* @param ms - milliseconds to sleep
|
|
7
|
+
*/
|
|
8
|
+
export declare function sleep(ms: number): Promise<void>;
|
|
9
|
+
/**
|
|
10
|
+
* Add a timeout to any promise
|
|
11
|
+
* @param promise - promise to add timeout to
|
|
12
|
+
* @param ms - timeout in milliseconds
|
|
13
|
+
* @param timeoutMessage - optional timeout error message
|
|
14
|
+
*/
|
|
15
|
+
export declare function timeout<T>(promise: Promise<T>, ms: number, timeoutMessage?: string): Promise<T>;
|
|
16
|
+
/**
|
|
17
|
+
* Debounce function - delays execution until after delay has passed since last call
|
|
18
|
+
* @param fn - function to debounce
|
|
19
|
+
* @param delay - delay in milliseconds
|
|
20
|
+
*/
|
|
21
|
+
export declare function debounce<T extends (...args: any[]) => any>(fn: T, delay: number): (...args: Parameters<T>) => void;
|
|
22
|
+
/**
|
|
23
|
+
* Throttle function - limits execution to once per delay period
|
|
24
|
+
* @param fn - function to throttle
|
|
25
|
+
* @param delay - delay in milliseconds
|
|
26
|
+
*/
|
|
27
|
+
export declare function throttle<T extends (...args: any[]) => any>(fn: T, delay: number): (...args: Parameters<T>) => void;
|
|
28
|
+
/**
|
|
29
|
+
* Retry a promise-returning function with exponential backoff
|
|
30
|
+
* @param fn - function that returns a promise
|
|
31
|
+
* @param maxAttempts - maximum number of attempts
|
|
32
|
+
* @param baseDelay - base delay in milliseconds
|
|
33
|
+
* @param maxDelay - maximum delay in milliseconds
|
|
34
|
+
*/
|
|
35
|
+
export declare function retry<T>(fn: () => Promise<T>, maxAttempts?: number, baseDelay?: number, maxDelay?: number): Promise<T>;
|
|
36
|
+
/**
|
|
37
|
+
* Stopwatch for measuring elapsed time
|
|
38
|
+
*/
|
|
39
|
+
export declare class Stopwatch {
|
|
40
|
+
private startTime;
|
|
41
|
+
private endTime;
|
|
42
|
+
private pausedTime;
|
|
43
|
+
private pauseStart;
|
|
44
|
+
/**
|
|
45
|
+
* Start the stopwatch
|
|
46
|
+
*/
|
|
47
|
+
start(): void;
|
|
48
|
+
/**
|
|
49
|
+
* Stop the stopwatch
|
|
50
|
+
*/
|
|
51
|
+
stop(): number;
|
|
52
|
+
/**
|
|
53
|
+
* Pause the stopwatch
|
|
54
|
+
*/
|
|
55
|
+
pause(): void;
|
|
56
|
+
/**
|
|
57
|
+
* Resume the stopwatch
|
|
58
|
+
*/
|
|
59
|
+
resume(): void;
|
|
60
|
+
/**
|
|
61
|
+
* Reset the stopwatch
|
|
62
|
+
*/
|
|
63
|
+
reset(): void;
|
|
64
|
+
/**
|
|
65
|
+
* Get elapsed time without stopping
|
|
66
|
+
*/
|
|
67
|
+
getElapsed(): number;
|
|
68
|
+
/**
|
|
69
|
+
* Check if stopwatch is running
|
|
70
|
+
*/
|
|
71
|
+
isRunning(): boolean;
|
|
72
|
+
/**
|
|
73
|
+
* Check if stopwatch is paused
|
|
74
|
+
*/
|
|
75
|
+
isPaused(): boolean;
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Create a new stopwatch instance
|
|
79
|
+
*/
|
|
80
|
+
export declare function createStopwatch(): Stopwatch;
|
|
81
|
+
/**
|
|
82
|
+
* Measure execution time of a synchronous function
|
|
83
|
+
* @param fn - function to measure
|
|
84
|
+
* @returns tuple of [result, elapsed time in ms]
|
|
85
|
+
*/
|
|
86
|
+
export declare function measureTime<T>(fn: () => T): [T, number];
|
|
87
|
+
/**
|
|
88
|
+
* Measure execution time of an asynchronous function
|
|
89
|
+
* @param fn - async function to measure
|
|
90
|
+
* @returns promise that resolves to tuple of [result, elapsed time in ms]
|
|
91
|
+
*/
|
|
92
|
+
export declare function measureAsync<T>(fn: () => Promise<T>): Promise<[T, number]>;
|
|
93
|
+
/**
|
|
94
|
+
* Benchmark result interface
|
|
95
|
+
*/
|
|
96
|
+
export interface BenchmarkResult {
|
|
97
|
+
totalTime: number;
|
|
98
|
+
averageTime: number;
|
|
99
|
+
minTime: number;
|
|
100
|
+
maxTime: number;
|
|
101
|
+
iterations: number;
|
|
102
|
+
opsPerSecond: number;
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Benchmark a function by running it multiple times
|
|
106
|
+
* @param fn - function to benchmark
|
|
107
|
+
* @param iterations - number of iterations to run
|
|
108
|
+
*/
|
|
109
|
+
export declare function benchmark(fn: () => void, iterations?: number): BenchmarkResult;
|
|
110
|
+
//# sourceMappingURL=performance.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"performance.d.ts","sourceRoot":"","sources":["../src/performance.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH;;;GAGG;AACH,wBAAgB,KAAK,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAE/C;AAED;;;;;GAKG;AACH,wBAAgB,OAAO,CAAC,CAAC,EACvB,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC,EACnB,EAAE,EAAE,MAAM,EACV,cAAc,SAAwB,GACrC,OAAO,CAAC,CAAC,CAAC,CAOZ;AAED;;;;GAIG;AACH,wBAAgB,QAAQ,CAAC,CAAC,SAAS,CAAC,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,GAAG,EACxD,EAAE,EAAE,CAAC,EACL,KAAK,EAAE,MAAM,GACZ,CAAC,GAAG,IAAI,EAAE,UAAU,CAAC,CAAC,CAAC,KAAK,IAAI,CAOlC;AAED;;;;GAIG;AACH,wBAAgB,QAAQ,CAAC,CAAC,SAAS,CAAC,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,GAAG,EACxD,EAAE,EAAE,CAAC,EACL,KAAK,EAAE,MAAM,GACZ,CAAC,GAAG,IAAI,EAAE,UAAU,CAAC,CAAC,CAAC,KAAK,IAAI,CAUlC;AAED;;;;;;GAMG;AACH,wBAAsB,KAAK,CAAC,CAAC,EAC3B,EAAE,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,EACpB,WAAW,GAAE,MAAU,EACvB,SAAS,GAAE,MAAa,EACxB,QAAQ,GAAE,MAAc,GACvB,OAAO,CAAC,CAAC,CAAC,CAwBZ;AAED;;GAEG;AACH,qBAAa,SAAS;IACpB,OAAO,CAAC,SAAS,CAAuB;IACxC,OAAO,CAAC,OAAO,CAAuB;IACtC,OAAO,CAAC,UAAU,CAAa;IAC/B,OAAO,CAAC,UAAU,CAAuB;IAEzC;;OAEG;IACH,KAAK,IAAI,IAAI;IAUb;;OAEG;IACH,IAAI,IAAI,MAAM;IAed;;OAEG;IACH,KAAK,IAAI,IAAI;IAUb;;OAEG;IACH,MAAM,IAAI,IAAI;IAQd;;OAEG;IACH,KAAK,IAAI,IAAI;IAOb;;OAEG;IACH,UAAU,IAAI,MAAM;IAepB;;OAEG;IACH,SAAS,IAAI,OAAO;IAIpB;;OAEG;IACH,QAAQ,IAAI,OAAO;CAGpB;AAED;;GAEG;AACH,wBAAgB,eAAe,IAAI,SAAS,CAE3C;AAED;;;;GAIG;AACH,wBAAgB,WAAW,CAAC,CAAC,EAAE,EAAE,EAAE,MAAM,CAAC,GAAG,CAAC,CAAC,EAAE,MAAM,CAAC,CAKvD;AAED;;;;GAIG;AACH,wBAAsB,YAAY,CAAC,CAAC,EAAE,EAAE,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC,CAKhF;AAED;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY,EAAE,MAAM,CAAC;CACtB;AAED;;;;GAIG;AACH,wBAAgB,SAAS,CAAC,EAAE,EAAE,MAAM,IAAI,EAAE,UAAU,GAAE,MAAa,GAAG,eAAe,CAsBpF"}
|
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Async time utilities for delays, timeouts, and performance
|
|
3
|
+
*/
|
|
4
|
+
/**
|
|
5
|
+
* Sleep for a specified number of milliseconds
|
|
6
|
+
* @param ms - milliseconds to sleep
|
|
7
|
+
*/
|
|
8
|
+
export function sleep(ms) {
|
|
9
|
+
return new Promise(resolve => setTimeout(resolve, ms));
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Add a timeout to any promise
|
|
13
|
+
* @param promise - promise to add timeout to
|
|
14
|
+
* @param ms - timeout in milliseconds
|
|
15
|
+
* @param timeoutMessage - optional timeout error message
|
|
16
|
+
*/
|
|
17
|
+
export function timeout(promise, ms, timeoutMessage = 'Operation timed out') {
|
|
18
|
+
return Promise.race([
|
|
19
|
+
promise,
|
|
20
|
+
new Promise((_, reject) => setTimeout(() => reject(new Error(timeoutMessage)), ms))
|
|
21
|
+
]);
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Debounce function - delays execution until after delay has passed since last call
|
|
25
|
+
* @param fn - function to debounce
|
|
26
|
+
* @param delay - delay in milliseconds
|
|
27
|
+
*/
|
|
28
|
+
export function debounce(fn, delay) {
|
|
29
|
+
let timeoutId;
|
|
30
|
+
return (...args) => {
|
|
31
|
+
clearTimeout(timeoutId);
|
|
32
|
+
timeoutId = setTimeout(() => fn(...args), delay);
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Throttle function - limits execution to once per delay period
|
|
37
|
+
* @param fn - function to throttle
|
|
38
|
+
* @param delay - delay in milliseconds
|
|
39
|
+
*/
|
|
40
|
+
export function throttle(fn, delay) {
|
|
41
|
+
let lastCall = 0;
|
|
42
|
+
return (...args) => {
|
|
43
|
+
const now = Date.now();
|
|
44
|
+
if (now - lastCall >= delay) {
|
|
45
|
+
lastCall = now;
|
|
46
|
+
fn(...args);
|
|
47
|
+
}
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Retry a promise-returning function with exponential backoff
|
|
52
|
+
* @param fn - function that returns a promise
|
|
53
|
+
* @param maxAttempts - maximum number of attempts
|
|
54
|
+
* @param baseDelay - base delay in milliseconds
|
|
55
|
+
* @param maxDelay - maximum delay in milliseconds
|
|
56
|
+
*/
|
|
57
|
+
export async function retry(fn, maxAttempts = 3, baseDelay = 1000, maxDelay = 10000) {
|
|
58
|
+
let lastError;
|
|
59
|
+
for (let attempt = 1; attempt <= maxAttempts; attempt++) {
|
|
60
|
+
try {
|
|
61
|
+
return await fn();
|
|
62
|
+
}
|
|
63
|
+
catch (error) {
|
|
64
|
+
lastError = error;
|
|
65
|
+
if (attempt === maxAttempts) {
|
|
66
|
+
throw lastError;
|
|
67
|
+
}
|
|
68
|
+
// Exponential backoff with jitter
|
|
69
|
+
const delay = Math.min(baseDelay * Math.pow(2, attempt - 1) + Math.random() * 1000, maxDelay);
|
|
70
|
+
await sleep(delay);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
throw lastError;
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Stopwatch for measuring elapsed time
|
|
77
|
+
*/
|
|
78
|
+
export class Stopwatch {
|
|
79
|
+
constructor() {
|
|
80
|
+
this.startTime = null;
|
|
81
|
+
this.endTime = null;
|
|
82
|
+
this.pausedTime = 0;
|
|
83
|
+
this.pauseStart = null;
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Start the stopwatch
|
|
87
|
+
*/
|
|
88
|
+
start() {
|
|
89
|
+
if (this.startTime !== null) {
|
|
90
|
+
throw new Error('Stopwatch is already running');
|
|
91
|
+
}
|
|
92
|
+
this.startTime = performance.now();
|
|
93
|
+
this.endTime = null;
|
|
94
|
+
this.pausedTime = 0;
|
|
95
|
+
this.pauseStart = null;
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Stop the stopwatch
|
|
99
|
+
*/
|
|
100
|
+
stop() {
|
|
101
|
+
if (this.startTime === null) {
|
|
102
|
+
throw new Error('Stopwatch is not running');
|
|
103
|
+
}
|
|
104
|
+
if (this.pauseStart !== null) {
|
|
105
|
+
this.pausedTime += performance.now() - this.pauseStart;
|
|
106
|
+
this.pauseStart = null;
|
|
107
|
+
}
|
|
108
|
+
this.endTime = performance.now();
|
|
109
|
+
const elapsed = this.endTime - this.startTime - this.pausedTime;
|
|
110
|
+
return elapsed;
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* Pause the stopwatch
|
|
114
|
+
*/
|
|
115
|
+
pause() {
|
|
116
|
+
if (this.startTime === null) {
|
|
117
|
+
throw new Error('Stopwatch is not running');
|
|
118
|
+
}
|
|
119
|
+
if (this.pauseStart !== null) {
|
|
120
|
+
throw new Error('Stopwatch is already paused');
|
|
121
|
+
}
|
|
122
|
+
this.pauseStart = performance.now();
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* Resume the stopwatch
|
|
126
|
+
*/
|
|
127
|
+
resume() {
|
|
128
|
+
if (this.pauseStart === null) {
|
|
129
|
+
throw new Error('Stopwatch is not paused');
|
|
130
|
+
}
|
|
131
|
+
this.pausedTime += performance.now() - this.pauseStart;
|
|
132
|
+
this.pauseStart = null;
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* Reset the stopwatch
|
|
136
|
+
*/
|
|
137
|
+
reset() {
|
|
138
|
+
this.startTime = null;
|
|
139
|
+
this.endTime = null;
|
|
140
|
+
this.pausedTime = 0;
|
|
141
|
+
this.pauseStart = null;
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* Get elapsed time without stopping
|
|
145
|
+
*/
|
|
146
|
+
getElapsed() {
|
|
147
|
+
if (this.startTime === null) {
|
|
148
|
+
return 0;
|
|
149
|
+
}
|
|
150
|
+
const now = performance.now();
|
|
151
|
+
let pausedTime = this.pausedTime;
|
|
152
|
+
if (this.pauseStart !== null) {
|
|
153
|
+
pausedTime += now - this.pauseStart;
|
|
154
|
+
}
|
|
155
|
+
return now - this.startTime - pausedTime;
|
|
156
|
+
}
|
|
157
|
+
/**
|
|
158
|
+
* Check if stopwatch is running
|
|
159
|
+
*/
|
|
160
|
+
isRunning() {
|
|
161
|
+
return this.startTime !== null && this.endTime === null && this.pauseStart === null;
|
|
162
|
+
}
|
|
163
|
+
/**
|
|
164
|
+
* Check if stopwatch is paused
|
|
165
|
+
*/
|
|
166
|
+
isPaused() {
|
|
167
|
+
return this.pauseStart !== null;
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
/**
|
|
171
|
+
* Create a new stopwatch instance
|
|
172
|
+
*/
|
|
173
|
+
export function createStopwatch() {
|
|
174
|
+
return new Stopwatch();
|
|
175
|
+
}
|
|
176
|
+
/**
|
|
177
|
+
* Measure execution time of a synchronous function
|
|
178
|
+
* @param fn - function to measure
|
|
179
|
+
* @returns tuple of [result, elapsed time in ms]
|
|
180
|
+
*/
|
|
181
|
+
export function measureTime(fn) {
|
|
182
|
+
const start = performance.now();
|
|
183
|
+
const result = fn();
|
|
184
|
+
const elapsed = performance.now() - start;
|
|
185
|
+
return [result, elapsed];
|
|
186
|
+
}
|
|
187
|
+
/**
|
|
188
|
+
* Measure execution time of an asynchronous function
|
|
189
|
+
* @param fn - async function to measure
|
|
190
|
+
* @returns promise that resolves to tuple of [result, elapsed time in ms]
|
|
191
|
+
*/
|
|
192
|
+
export async function measureAsync(fn) {
|
|
193
|
+
const start = performance.now();
|
|
194
|
+
const result = await fn();
|
|
195
|
+
const elapsed = performance.now() - start;
|
|
196
|
+
return [result, elapsed];
|
|
197
|
+
}
|
|
198
|
+
/**
|
|
199
|
+
* Benchmark a function by running it multiple times
|
|
200
|
+
* @param fn - function to benchmark
|
|
201
|
+
* @param iterations - number of iterations to run
|
|
202
|
+
*/
|
|
203
|
+
export function benchmark(fn, iterations = 1000) {
|
|
204
|
+
const times = [];
|
|
205
|
+
for (let i = 0; i < iterations; i++) {
|
|
206
|
+
const [, elapsed] = measureTime(fn);
|
|
207
|
+
times.push(elapsed);
|
|
208
|
+
}
|
|
209
|
+
const totalTime = times.reduce((sum, time) => sum + time, 0);
|
|
210
|
+
const averageTime = totalTime / iterations;
|
|
211
|
+
const minTime = Math.min(...times);
|
|
212
|
+
const maxTime = Math.max(...times);
|
|
213
|
+
const opsPerSecond = 1000 / averageTime;
|
|
214
|
+
return {
|
|
215
|
+
totalTime,
|
|
216
|
+
averageTime,
|
|
217
|
+
minTime,
|
|
218
|
+
maxTime,
|
|
219
|
+
iterations,
|
|
220
|
+
opsPerSecond
|
|
221
|
+
};
|
|
222
|
+
}
|