ts-time-utils 4.0.1 → 4.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +175 -30
- package/dist/{age.js → age.cjs} +14 -6
- package/dist/{calculate.js → calculate.cjs} +30 -18
- package/dist/{calendar.js → calendar.cjs} +80 -39
- package/dist/{calendars.js → calendars.cjs} +48 -23
- package/dist/{chain.js → chain.cjs} +41 -40
- package/dist/{compare.js → compare.cjs} +58 -28
- package/dist/constants.cjs +19 -0
- package/dist/{countdown.js → countdown.cjs} +16 -7
- package/dist/{cron.js → cron.cjs} +20 -9
- package/dist/{dateRange.js → dateRange.cjs} +42 -26
- package/dist/{duration.js → duration.cjs} +56 -44
- package/dist/esm/chain.js +0 -5
- package/dist/esm/finance.d.ts +236 -0
- package/dist/esm/finance.d.ts.map +1 -0
- package/dist/esm/finance.js +495 -0
- package/dist/esm/healthcare.d.ts +260 -0
- package/dist/esm/healthcare.d.ts.map +1 -0
- package/dist/esm/healthcare.js +447 -0
- package/dist/esm/index.d.ts +6 -0
- package/dist/esm/index.d.ts.map +1 -1
- package/dist/esm/index.js +6 -0
- package/dist/esm/naturalLanguage.d.ts +1 -3
- package/dist/esm/naturalLanguage.d.ts.map +1 -1
- package/dist/esm/naturalLanguage.js +9 -2
- package/dist/esm/plugins.d.ts +0 -6
- package/dist/esm/plugins.d.ts.map +1 -1
- package/dist/esm/plugins.js +36 -42
- package/dist/esm/recurrence.d.ts.map +1 -1
- package/dist/esm/recurrence.js +3 -5
- package/dist/esm/scheduling.d.ts +206 -0
- package/dist/esm/scheduling.d.ts.map +1 -0
- package/dist/esm/scheduling.js +329 -0
- package/dist/esm/timezone.d.ts +6 -1
- package/dist/esm/timezone.d.ts.map +1 -1
- package/dist/esm/timezone.js +106 -66
- package/dist/esm/types.d.ts +0 -4
- package/dist/esm/types.d.ts.map +1 -1
- package/dist/finance.cjs +512 -0
- package/dist/finance.d.ts +236 -0
- package/dist/finance.d.ts.map +1 -0
- package/dist/{fiscal.js → fiscal.cjs} +36 -17
- package/dist/{format.js → format.cjs} +83 -70
- package/dist/healthcare.cjs +462 -0
- package/dist/healthcare.d.ts +260 -0
- package/dist/healthcare.d.ts.map +1 -0
- package/dist/{holidays.js → holidays.cjs} +52 -25
- package/dist/index.cjs +595 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/{interval.js → interval.cjs} +24 -11
- package/dist/{iterate.js → iterate.cjs} +84 -41
- package/dist/{locale.js → locale.cjs} +54 -26
- package/dist/{naturalLanguage.js → naturalLanguage.cjs} +36 -23
- package/dist/naturalLanguage.d.ts +1 -3
- package/dist/naturalLanguage.d.ts.map +1 -1
- package/dist/{parse.js → parse.cjs} +24 -11
- package/dist/{performance.js → performance.cjs} +23 -10
- package/dist/{plugins.js → plugins.cjs} +48 -47
- package/dist/plugins.d.ts +0 -6
- package/dist/plugins.d.ts.map +1 -1
- package/dist/{precision.js → precision.cjs} +74 -37
- package/dist/{rangePresets.js → rangePresets.cjs} +40 -19
- package/dist/{recurrence.js → recurrence.cjs} +27 -21
- package/dist/recurrence.d.ts.map +1 -1
- package/dist/scheduling.cjs +344 -0
- package/dist/scheduling.d.ts +206 -0
- package/dist/scheduling.d.ts.map +1 -0
- package/dist/{serialize.js → serialize.cjs} +36 -17
- package/dist/{temporal.js → temporal.cjs} +28 -13
- package/dist/{timezone.js → timezone.cjs} +140 -82
- package/dist/timezone.d.ts +6 -1
- package/dist/timezone.d.ts.map +1 -1
- package/dist/{types.js → types.cjs} +9 -3
- package/dist/types.d.ts +0 -4
- package/dist/types.d.ts.map +1 -1
- package/dist/{validate.js → validate.cjs} +54 -26
- package/dist/{workingHours.js → workingHours.cjs} +36 -17
- package/package.json +52 -34
- package/dist/constants.js +0 -16
- package/dist/index.js +0 -66
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
"use strict";
|
|
1
2
|
/**
|
|
2
3
|
* Plugin system for extending ChainedDate with custom methods
|
|
3
4
|
*
|
|
@@ -19,38 +20,26 @@
|
|
|
19
20
|
* chain(new Date()).nextMonday().format('YYYY-MM-DD');
|
|
20
21
|
* ```
|
|
21
22
|
*/
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
23
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
24
|
+
exports.extend = extend;
|
|
25
|
+
exports.uninstall = uninstall;
|
|
26
|
+
exports.getRegisteredPlugins = getRegisteredPlugins;
|
|
27
|
+
exports.getPluginMethods = getPluginMethods;
|
|
28
|
+
exports.isPluginRegistered = isPluginRegistered;
|
|
29
|
+
const chain_js_1 = require("./chain.cjs");
|
|
25
30
|
/**
|
|
26
|
-
* Get ChainedDate class
|
|
31
|
+
* Get ChainedDate class for prototype mutation.
|
|
32
|
+
* Importing `plugins` now brings in `chain` directly, so the class is available
|
|
33
|
+
* without a hidden global handshake.
|
|
27
34
|
*/
|
|
28
35
|
function getChainedDate() {
|
|
29
|
-
if (!
|
|
30
|
-
|
|
31
|
-
try {
|
|
32
|
-
// Try ESM import path first
|
|
33
|
-
ChainedDateConstructor = globalThis.__chainedDateClass;
|
|
34
|
-
if (!ChainedDateConstructor) {
|
|
35
|
-
// Fallback: The class will be set by chain.js when it loads
|
|
36
|
-
throw new Error('ChainedDate not yet loaded. Import chain.js before using plugins.');
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
catch (e) {
|
|
40
|
-
throw new Error('ChainedDate class not available. Ensure chain.js is imported before registering plugins.');
|
|
41
|
-
}
|
|
36
|
+
if (!chain_js_1.ChainedDate) {
|
|
37
|
+
throw new Error('ChainedDate export is not available yet. Import chain.js before registering plugins.');
|
|
42
38
|
}
|
|
43
|
-
return
|
|
44
|
-
}
|
|
45
|
-
/**
|
|
46
|
-
* Initialize the plugin system with ChainedDate class
|
|
47
|
-
* This is called automatically when chain.js is imported
|
|
48
|
-
* @internal
|
|
49
|
-
*/
|
|
50
|
-
export function __initPluginSystem(ChainedDateClass) {
|
|
51
|
-
ChainedDateConstructor = ChainedDateClass;
|
|
39
|
+
return chain_js_1.ChainedDate;
|
|
52
40
|
}
|
|
53
41
|
const registry = {};
|
|
42
|
+
const methodStates = {};
|
|
54
43
|
/**
|
|
55
44
|
* Extend ChainedDate with custom methods
|
|
56
45
|
*
|
|
@@ -76,27 +65,27 @@ const registry = {};
|
|
|
76
65
|
* chain(new Date()).addBusinessDays(5);
|
|
77
66
|
* ```
|
|
78
67
|
*/
|
|
79
|
-
|
|
68
|
+
function extend(pluginName, methods) {
|
|
80
69
|
if (registry[pluginName]) {
|
|
81
70
|
throw new Error(`Plugin "${pluginName}" is already registered. Use a different name or uninstall first.`);
|
|
82
71
|
}
|
|
83
|
-
// Get ChainedDate class and save original methods before overwriting
|
|
84
72
|
const ChainedDateClass = getChainedDate();
|
|
85
|
-
const originalMethods = new Map();
|
|
86
73
|
Object.entries(methods).forEach(([methodName, fn]) => {
|
|
87
|
-
|
|
74
|
+
const current = ChainedDateClass.prototype[methodName];
|
|
75
|
+
if (!methodStates[methodName]) {
|
|
76
|
+
methodStates[methodName] = {
|
|
77
|
+
original: typeof current === 'function' ? current : undefined,
|
|
78
|
+
owners: []
|
|
79
|
+
};
|
|
80
|
+
}
|
|
88
81
|
if (methodName in ChainedDateClass.prototype) {
|
|
89
|
-
const original = ChainedDateClass.prototype[methodName];
|
|
90
|
-
if (typeof original === 'function') {
|
|
91
|
-
originalMethods.set(methodName, original);
|
|
92
|
-
}
|
|
93
82
|
console.warn(`Method "${methodName}" already exists on ChainedDate and will be overwritten`);
|
|
94
83
|
}
|
|
84
|
+
methodStates[methodName].owners.push(pluginName);
|
|
95
85
|
// Add the plugin method
|
|
96
86
|
ChainedDateClass.prototype[methodName] = fn;
|
|
97
87
|
});
|
|
98
|
-
|
|
99
|
-
registry[pluginName] = { plugin: methods, originalMethods };
|
|
88
|
+
registry[pluginName] = { plugin: methods };
|
|
100
89
|
}
|
|
101
90
|
/**
|
|
102
91
|
* Remove a plugin and its methods from ChainedDate
|
|
@@ -108,25 +97,37 @@ export function extend(pluginName, methods) {
|
|
|
108
97
|
* uninstall('businessDays');
|
|
109
98
|
* ```
|
|
110
99
|
*/
|
|
111
|
-
|
|
100
|
+
function uninstall(pluginName) {
|
|
112
101
|
const entry = registry[pluginName];
|
|
113
102
|
if (!entry) {
|
|
114
103
|
throw new Error(`Plugin "${pluginName}" is not registered`);
|
|
115
104
|
}
|
|
116
|
-
// Get ChainedDate class and restore/remove methods
|
|
117
105
|
const ChainedDateClass = getChainedDate();
|
|
118
106
|
Object.keys(entry.plugin).forEach((methodName) => {
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
107
|
+
const state = methodStates[methodName];
|
|
108
|
+
if (!state) {
|
|
109
|
+
delete ChainedDateClass.prototype[methodName];
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
state.owners = state.owners.filter((owner) => owner !== pluginName);
|
|
113
|
+
const nextOwner = state.owners[state.owners.length - 1];
|
|
114
|
+
if (nextOwner) {
|
|
115
|
+
const nextPlugin = registry[nextOwner];
|
|
116
|
+
if (nextPlugin && methodName in nextPlugin.plugin) {
|
|
117
|
+
ChainedDateClass.prototype[methodName] = nextPlugin.plugin[methodName];
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
delete ChainedDateClass.prototype[methodName];
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
123
|
+
if (state.original) {
|
|
124
|
+
ChainedDateClass.prototype[methodName] = state.original;
|
|
123
125
|
}
|
|
124
126
|
else {
|
|
125
|
-
// Otherwise, delete the method entirely
|
|
126
127
|
delete ChainedDateClass.prototype[methodName];
|
|
127
128
|
}
|
|
129
|
+
delete methodStates[methodName];
|
|
128
130
|
});
|
|
129
|
-
// Remove from registry
|
|
130
131
|
delete registry[pluginName];
|
|
131
132
|
}
|
|
132
133
|
/**
|
|
@@ -139,7 +140,7 @@ export function uninstall(pluginName) {
|
|
|
139
140
|
* getRegisteredPlugins(); // ['businessDays', 'myPlugin']
|
|
140
141
|
* ```
|
|
141
142
|
*/
|
|
142
|
-
|
|
143
|
+
function getRegisteredPlugins() {
|
|
143
144
|
return Object.keys(registry);
|
|
144
145
|
}
|
|
145
146
|
/**
|
|
@@ -153,7 +154,7 @@ export function getRegisteredPlugins() {
|
|
|
153
154
|
* getPluginMethods('businessDays'); // ['addBusinessDays', 'subtractBusinessDays']
|
|
154
155
|
* ```
|
|
155
156
|
*/
|
|
156
|
-
|
|
157
|
+
function getPluginMethods(pluginName) {
|
|
157
158
|
const entry = registry[pluginName];
|
|
158
159
|
return entry ? Object.keys(entry.plugin) : [];
|
|
159
160
|
}
|
|
@@ -168,6 +169,6 @@ export function getPluginMethods(pluginName) {
|
|
|
168
169
|
* isPluginRegistered('businessDays'); // true
|
|
169
170
|
* ```
|
|
170
171
|
*/
|
|
171
|
-
|
|
172
|
+
function isPluginRegistered(pluginName) {
|
|
172
173
|
return pluginName in registry;
|
|
173
174
|
}
|
package/dist/plugins.d.ts
CHANGED
|
@@ -19,12 +19,6 @@
|
|
|
19
19
|
* chain(new Date()).nextMonday().format('YYYY-MM-DD');
|
|
20
20
|
* ```
|
|
21
21
|
*/
|
|
22
|
-
/**
|
|
23
|
-
* Initialize the plugin system with ChainedDate class
|
|
24
|
-
* This is called automatically when chain.js is imported
|
|
25
|
-
* @internal
|
|
26
|
-
*/
|
|
27
|
-
export declare function __initPluginSystem(ChainedDateClass: any): void;
|
|
28
22
|
/**
|
|
29
23
|
* Plugin function type - methods receive ChainedDate instance as `this`
|
|
30
24
|
*/
|
package/dist/plugins.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"plugins.d.ts","sourceRoot":"","sources":["../src/plugins.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;
|
|
1
|
+
{"version":3,"file":"plugins.d.ts","sourceRoot":"","sources":["../src/plugins.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAgBH;;GAEG;AACH,MAAM,MAAM,cAAc,GAAG,CAAC,IAAI,EAAE,GAAG,EAAE,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,GAAG,CAAC;AAEhE;;GAEG;AACH,MAAM,WAAW,MAAM;IACrB,CAAC,UAAU,EAAE,MAAM,GAAG,cAAc,CAAC;CACtC;AAoBD;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,wBAAgB,MAAM,CAAC,UAAU,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,IAAI,CAwBhE;AAED;;;;;;;;;GASG;AACH,wBAAgB,SAAS,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI,CAqClD;AAED;;;;;;;;;GASG;AACH,wBAAgB,oBAAoB,IAAI,MAAM,EAAE,CAE/C;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,gBAAgB,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,EAAE,CAG7D;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,kBAAkB,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAE9D;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,WAAW,iBAAiB;CAAG"}
|
|
@@ -1,11 +1,47 @@
|
|
|
1
|
+
"use strict";
|
|
1
2
|
/**
|
|
2
3
|
* @fileoverview High-precision time utilities
|
|
3
4
|
* Handles nanoseconds, BigInt timestamps, sub-millisecond operations
|
|
4
5
|
*/
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
exports.LEAP_SECONDS = exports.ValidDate = void 0;
|
|
8
|
+
exports.createNanosecondTimestamp = createNanosecondTimestamp;
|
|
9
|
+
exports.fromNanoseconds = fromNanoseconds;
|
|
10
|
+
exports.dateToNanoseconds = dateToNanoseconds;
|
|
11
|
+
exports.nanosecondsToDate = nanosecondsToDate;
|
|
12
|
+
exports.addNanoseconds = addNanoseconds;
|
|
13
|
+
exports.subtractNanoseconds = subtractNanoseconds;
|
|
14
|
+
exports.compareNanoseconds = compareNanoseconds;
|
|
15
|
+
exports.nowNanoseconds = nowNanoseconds;
|
|
16
|
+
exports.formatNanoseconds = formatNanoseconds;
|
|
17
|
+
exports.parseNanoseconds = parseNanoseconds;
|
|
18
|
+
exports.createHighResDuration = createHighResDuration;
|
|
19
|
+
exports.addHighResDuration = addHighResDuration;
|
|
20
|
+
exports.subtractHighResDuration = subtractHighResDuration;
|
|
21
|
+
exports.highResDurationToMs = highResDurationToMs;
|
|
22
|
+
exports.msToHighResDuration = msToHighResDuration;
|
|
23
|
+
exports.toBigIntMs = toBigIntMs;
|
|
24
|
+
exports.fromBigIntMs = fromBigIntMs;
|
|
25
|
+
exports.toBigIntSeconds = toBigIntSeconds;
|
|
26
|
+
exports.fromBigIntSeconds = fromBigIntSeconds;
|
|
27
|
+
exports.addBigIntMs = addBigIntMs;
|
|
28
|
+
exports.subtractBigIntMs = subtractBigIntMs;
|
|
29
|
+
exports.diffBigIntMs = diffBigIntMs;
|
|
30
|
+
exports.isInDSTGap = isInDSTGap;
|
|
31
|
+
exports.isInDSTOverlap = isInDSTOverlap;
|
|
32
|
+
exports.getDSTTransitionsInYear = getDSTTransitionsInYear;
|
|
33
|
+
exports.resolveAmbiguousTime = resolveAmbiguousTime;
|
|
34
|
+
exports.ensureValidDate = ensureValidDate;
|
|
35
|
+
exports.parseValidDate = parseValidDate;
|
|
36
|
+
exports.assertValidDate = assertValidDate;
|
|
37
|
+
exports.leapSecondsBetween = leapSecondsBetween;
|
|
38
|
+
exports.isNearLeapSecond = isNearLeapSecond;
|
|
39
|
+
exports.taiToUtc = taiToUtc;
|
|
40
|
+
exports.utcToTai = utcToTai;
|
|
5
41
|
/**
|
|
6
42
|
* Create a nanosecond timestamp from components
|
|
7
43
|
*/
|
|
8
|
-
|
|
44
|
+
function createNanosecondTimestamp(milliseconds, nanoseconds = 0) {
|
|
9
45
|
// Normalize: ensure nanoseconds is 0-999999
|
|
10
46
|
const extraMs = Math.floor(nanoseconds / 1000000);
|
|
11
47
|
const normalizedNs = nanoseconds % 1000000;
|
|
@@ -20,7 +56,7 @@ export function createNanosecondTimestamp(milliseconds, nanoseconds = 0) {
|
|
|
20
56
|
/**
|
|
21
57
|
* Create a nanosecond timestamp from BigInt
|
|
22
58
|
*/
|
|
23
|
-
|
|
59
|
+
function fromNanoseconds(totalNs) {
|
|
24
60
|
const ms = Number(totalNs / BigInt(1000000));
|
|
25
61
|
const ns = Number(totalNs % BigInt(1000000));
|
|
26
62
|
return {
|
|
@@ -32,32 +68,32 @@ export function fromNanoseconds(totalNs) {
|
|
|
32
68
|
/**
|
|
33
69
|
* Create a nanosecond timestamp from a Date
|
|
34
70
|
*/
|
|
35
|
-
|
|
71
|
+
function dateToNanoseconds(date) {
|
|
36
72
|
return createNanosecondTimestamp(date.getTime(), 0);
|
|
37
73
|
}
|
|
38
74
|
/**
|
|
39
75
|
* Convert nanosecond timestamp to Date (loses sub-millisecond precision)
|
|
40
76
|
*/
|
|
41
|
-
|
|
77
|
+
function nanosecondsToDate(timestamp) {
|
|
42
78
|
return new Date(timestamp.milliseconds);
|
|
43
79
|
}
|
|
44
80
|
/**
|
|
45
81
|
* Add two nanosecond timestamps
|
|
46
82
|
*/
|
|
47
|
-
|
|
83
|
+
function addNanoseconds(a, b) {
|
|
48
84
|
return fromNanoseconds(a.totalNanoseconds + b.totalNanoseconds);
|
|
49
85
|
}
|
|
50
86
|
/**
|
|
51
87
|
* Subtract nanosecond timestamps
|
|
52
88
|
*/
|
|
53
|
-
|
|
89
|
+
function subtractNanoseconds(a, b) {
|
|
54
90
|
return fromNanoseconds(a.totalNanoseconds - b.totalNanoseconds);
|
|
55
91
|
}
|
|
56
92
|
/**
|
|
57
93
|
* Compare nanosecond timestamps
|
|
58
94
|
* @returns -1 if a < b, 0 if equal, 1 if a > b
|
|
59
95
|
*/
|
|
60
|
-
|
|
96
|
+
function compareNanoseconds(a, b) {
|
|
61
97
|
if (a.totalNanoseconds < b.totalNanoseconds)
|
|
62
98
|
return -1;
|
|
63
99
|
if (a.totalNanoseconds > b.totalNanoseconds)
|
|
@@ -68,7 +104,7 @@ export function compareNanoseconds(a, b) {
|
|
|
68
104
|
* Get current time with nanosecond precision (if available)
|
|
69
105
|
* Falls back to millisecond precision if performance.now() not available
|
|
70
106
|
*/
|
|
71
|
-
|
|
107
|
+
function nowNanoseconds() {
|
|
72
108
|
const now = Date.now();
|
|
73
109
|
// Try to get sub-millisecond precision from performance.now()
|
|
74
110
|
if (typeof performance !== 'undefined' && performance.now) {
|
|
@@ -81,7 +117,7 @@ export function nowNanoseconds() {
|
|
|
81
117
|
/**
|
|
82
118
|
* Format nanosecond timestamp to ISO string with sub-millisecond precision
|
|
83
119
|
*/
|
|
84
|
-
|
|
120
|
+
function formatNanoseconds(timestamp) {
|
|
85
121
|
const date = nanosecondsToDate(timestamp);
|
|
86
122
|
const isoBase = date.toISOString().slice(0, -1); // Remove trailing 'Z'
|
|
87
123
|
// Add nanoseconds (6 additional digits after milliseconds)
|
|
@@ -91,7 +127,7 @@ export function formatNanoseconds(timestamp) {
|
|
|
91
127
|
/**
|
|
92
128
|
* Parse ISO string with nanosecond precision
|
|
93
129
|
*/
|
|
94
|
-
|
|
130
|
+
function parseNanoseconds(isoString) {
|
|
95
131
|
// Match ISO format: 2024-03-25T14:30:45.123456789Z
|
|
96
132
|
const match = isoString.match(/^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})\.(\d{3,9})Z?$/);
|
|
97
133
|
if (!match) {
|
|
@@ -114,7 +150,7 @@ export function parseNanoseconds(isoString) {
|
|
|
114
150
|
/**
|
|
115
151
|
* Create a high-resolution duration
|
|
116
152
|
*/
|
|
117
|
-
|
|
153
|
+
function createHighResDuration(seconds, nanoseconds = 0) {
|
|
118
154
|
// Normalize: carry over extra seconds from nanoseconds
|
|
119
155
|
const extraSeconds = Math.floor(nanoseconds / 1000000000);
|
|
120
156
|
const normalizedNs = nanoseconds % 1000000000;
|
|
@@ -126,7 +162,7 @@ export function createHighResDuration(seconds, nanoseconds = 0) {
|
|
|
126
162
|
/**
|
|
127
163
|
* Add two high-resolution durations
|
|
128
164
|
*/
|
|
129
|
-
|
|
165
|
+
function addHighResDuration(a, b) {
|
|
130
166
|
const totalNs = a.nanoseconds + b.nanoseconds;
|
|
131
167
|
const extraSeconds = Math.floor(totalNs / 1000000000);
|
|
132
168
|
const ns = totalNs % 1000000000;
|
|
@@ -138,7 +174,7 @@ export function addHighResDuration(a, b) {
|
|
|
138
174
|
/**
|
|
139
175
|
* Subtract high-resolution durations
|
|
140
176
|
*/
|
|
141
|
-
|
|
177
|
+
function subtractHighResDuration(a, b) {
|
|
142
178
|
let totalNs = a.nanoseconds - b.nanoseconds;
|
|
143
179
|
let seconds = a.seconds - b.seconds;
|
|
144
180
|
if (totalNs < 0) {
|
|
@@ -150,13 +186,13 @@ export function subtractHighResDuration(a, b) {
|
|
|
150
186
|
/**
|
|
151
187
|
* Convert high-resolution duration to milliseconds
|
|
152
188
|
*/
|
|
153
|
-
|
|
189
|
+
function highResDurationToMs(duration) {
|
|
154
190
|
return duration.seconds * 1000 + duration.nanoseconds / 1000000;
|
|
155
191
|
}
|
|
156
192
|
/**
|
|
157
193
|
* Convert milliseconds to high-resolution duration
|
|
158
194
|
*/
|
|
159
|
-
|
|
195
|
+
function msToHighResDuration(ms) {
|
|
160
196
|
const seconds = Math.floor(ms / 1000);
|
|
161
197
|
const nanoseconds = Math.round((ms % 1000) * 1000000);
|
|
162
198
|
return { seconds, nanoseconds };
|
|
@@ -165,45 +201,45 @@ export function msToHighResDuration(ms) {
|
|
|
165
201
|
/**
|
|
166
202
|
* Convert Unix epoch milliseconds to BigInt
|
|
167
203
|
*/
|
|
168
|
-
|
|
204
|
+
function toBigIntMs(date) {
|
|
169
205
|
return BigInt(date.getTime());
|
|
170
206
|
}
|
|
171
207
|
/**
|
|
172
208
|
* Convert BigInt milliseconds to Date
|
|
173
209
|
*/
|
|
174
|
-
|
|
210
|
+
function fromBigIntMs(ms) {
|
|
175
211
|
return new Date(Number(ms));
|
|
176
212
|
}
|
|
177
213
|
/**
|
|
178
214
|
* Convert Unix epoch seconds to BigInt
|
|
179
215
|
*/
|
|
180
|
-
|
|
216
|
+
function toBigIntSeconds(date) {
|
|
181
217
|
return BigInt(Math.floor(date.getTime() / 1000));
|
|
182
218
|
}
|
|
183
219
|
/**
|
|
184
220
|
* Convert BigInt seconds to Date
|
|
185
221
|
*/
|
|
186
|
-
|
|
222
|
+
function fromBigIntSeconds(seconds) {
|
|
187
223
|
return new Date(Number(seconds) * 1000);
|
|
188
224
|
}
|
|
189
225
|
/**
|
|
190
226
|
* Add milliseconds (as BigInt) to a date
|
|
191
227
|
*/
|
|
192
|
-
|
|
228
|
+
function addBigIntMs(date, ms) {
|
|
193
229
|
const current = toBigIntMs(date);
|
|
194
230
|
return fromBigIntMs(current + ms);
|
|
195
231
|
}
|
|
196
232
|
/**
|
|
197
233
|
* Subtract milliseconds (as BigInt) from a date
|
|
198
234
|
*/
|
|
199
|
-
|
|
235
|
+
function subtractBigIntMs(date, ms) {
|
|
200
236
|
const current = toBigIntMs(date);
|
|
201
237
|
return fromBigIntMs(current - ms);
|
|
202
238
|
}
|
|
203
239
|
/**
|
|
204
240
|
* Calculate difference between dates in BigInt milliseconds
|
|
205
241
|
*/
|
|
206
|
-
|
|
242
|
+
function diffBigIntMs(a, b) {
|
|
207
243
|
return toBigIntMs(a) - toBigIntMs(b);
|
|
208
244
|
}
|
|
209
245
|
/**
|
|
@@ -211,7 +247,7 @@ export function diffBigIntMs(a, b) {
|
|
|
211
247
|
* @param date - Date to check
|
|
212
248
|
* @param timeZone - IANA timezone (optional, uses local if not provided)
|
|
213
249
|
*/
|
|
214
|
-
|
|
250
|
+
function isInDSTGap(date, timeZone) {
|
|
215
251
|
if (timeZone) {
|
|
216
252
|
// For specific timezone, we need to check around this time
|
|
217
253
|
const formatter = new Intl.DateTimeFormat('en-US', {
|
|
@@ -255,7 +291,7 @@ export function isInDSTGap(date, timeZone) {
|
|
|
255
291
|
* @param date - Date to check
|
|
256
292
|
* @param timeZone - IANA timezone (optional, uses local if not provided)
|
|
257
293
|
*/
|
|
258
|
-
|
|
294
|
+
function isInDSTOverlap(date, timeZone) {
|
|
259
295
|
if (timeZone) {
|
|
260
296
|
// Similar approach as isInDSTGap but for fall-back
|
|
261
297
|
// This is harder to detect accurately without full TZ database
|
|
@@ -272,7 +308,7 @@ export function isInDSTOverlap(date, timeZone) {
|
|
|
272
308
|
/**
|
|
273
309
|
* Find DST transitions in a year for local timezone
|
|
274
310
|
*/
|
|
275
|
-
|
|
311
|
+
function getDSTTransitionsInYear(year) {
|
|
276
312
|
const transitions = [];
|
|
277
313
|
let prevOffset = new Date(year, 0, 1).getTimezoneOffset();
|
|
278
314
|
// Check each day of the year
|
|
@@ -312,7 +348,7 @@ export function getDSTTransitionsInYear(year) {
|
|
|
312
348
|
* @param date - Potentially ambiguous date
|
|
313
349
|
* @param prefer - Prefer 'earlier' or 'later' interpretation
|
|
314
350
|
*/
|
|
315
|
-
|
|
351
|
+
function resolveAmbiguousTime(date, prefer = 'earlier') {
|
|
316
352
|
if (!isInDSTOverlap(date)) {
|
|
317
353
|
return date;
|
|
318
354
|
}
|
|
@@ -333,7 +369,7 @@ export function resolveAmbiguousTime(date, prefer = 'earlier') {
|
|
|
333
369
|
/**
|
|
334
370
|
* Validated Date wrapper that guarantees a valid date
|
|
335
371
|
*/
|
|
336
|
-
|
|
372
|
+
class ValidDate {
|
|
337
373
|
constructor(date) {
|
|
338
374
|
this._date = date;
|
|
339
375
|
}
|
|
@@ -394,16 +430,17 @@ export class ValidDate {
|
|
|
394
430
|
return this._date.toLocaleString(locale, options);
|
|
395
431
|
}
|
|
396
432
|
}
|
|
433
|
+
exports.ValidDate = ValidDate;
|
|
397
434
|
/**
|
|
398
435
|
* Ensure a date is valid, with fallback
|
|
399
436
|
*/
|
|
400
|
-
|
|
437
|
+
function ensureValidDate(date, fallback = new Date()) {
|
|
401
438
|
return isNaN(date.getTime()) ? fallback : date;
|
|
402
439
|
}
|
|
403
440
|
/**
|
|
404
441
|
* Parse date with validation
|
|
405
442
|
*/
|
|
406
|
-
|
|
443
|
+
function parseValidDate(input) {
|
|
407
444
|
if (input instanceof Date) {
|
|
408
445
|
return isNaN(input.getTime()) ? null : input;
|
|
409
446
|
}
|
|
@@ -413,7 +450,7 @@ export function parseValidDate(input) {
|
|
|
413
450
|
/**
|
|
414
451
|
* Assert date is valid, throws if not
|
|
415
452
|
*/
|
|
416
|
-
|
|
453
|
+
function assertValidDate(date, message) {
|
|
417
454
|
if (isNaN(date.getTime())) {
|
|
418
455
|
throw new Error(message || 'Invalid Date');
|
|
419
456
|
}
|
|
@@ -423,7 +460,7 @@ export function assertValidDate(date, message) {
|
|
|
423
460
|
* Known leap seconds (added at end of these dates, 23:59:60 UTC)
|
|
424
461
|
* List from https://www.ietf.org/timezones/data/leap-seconds.list
|
|
425
462
|
*/
|
|
426
|
-
|
|
463
|
+
exports.LEAP_SECONDS = [
|
|
427
464
|
new Date('1972-06-30T23:59:59Z'),
|
|
428
465
|
new Date('1972-12-31T23:59:59Z'),
|
|
429
466
|
new Date('1973-12-31T23:59:59Z'),
|
|
@@ -455,9 +492,9 @@ export const LEAP_SECONDS = [
|
|
|
455
492
|
/**
|
|
456
493
|
* Get number of leap seconds between two dates
|
|
457
494
|
*/
|
|
458
|
-
|
|
495
|
+
function leapSecondsBetween(start, end) {
|
|
459
496
|
let count = 0;
|
|
460
|
-
for (const ls of LEAP_SECONDS) {
|
|
497
|
+
for (const ls of exports.LEAP_SECONDS) {
|
|
461
498
|
if (ls >= start && ls < end) {
|
|
462
499
|
count++;
|
|
463
500
|
}
|
|
@@ -467,15 +504,15 @@ export function leapSecondsBetween(start, end) {
|
|
|
467
504
|
/**
|
|
468
505
|
* Check if a date is near a leap second (within 1 second)
|
|
469
506
|
*/
|
|
470
|
-
|
|
507
|
+
function isNearLeapSecond(date) {
|
|
471
508
|
const time = date.getTime();
|
|
472
|
-
return LEAP_SECONDS.some(ls => Math.abs(ls.getTime() - time) <= 1000);
|
|
509
|
+
return exports.LEAP_SECONDS.some(ls => Math.abs(ls.getTime() - time) <= 1000);
|
|
473
510
|
}
|
|
474
511
|
/**
|
|
475
512
|
* Convert TAI (International Atomic Time) to UTC
|
|
476
513
|
* TAI = UTC + accumulated leap seconds + 10 (initial offset)
|
|
477
514
|
*/
|
|
478
|
-
|
|
515
|
+
function taiToUtc(taiMs) {
|
|
479
516
|
// Count leap seconds before this TAI time
|
|
480
517
|
const utcApprox = new Date(taiMs);
|
|
481
518
|
const leapSeconds = leapSecondsBetween(new Date(0), utcApprox);
|
|
@@ -485,7 +522,7 @@ export function taiToUtc(taiMs) {
|
|
|
485
522
|
/**
|
|
486
523
|
* Convert UTC to TAI
|
|
487
524
|
*/
|
|
488
|
-
|
|
525
|
+
function utcToTai(date) {
|
|
489
526
|
const leapSeconds = leapSecondsBetween(new Date(0), date);
|
|
490
527
|
return date.getTime() + (leapSeconds + 10) * 1000;
|
|
491
528
|
}
|
|
@@ -1,6 +1,27 @@
|
|
|
1
|
+
"use strict";
|
|
1
2
|
/**
|
|
2
3
|
* Predefined date range helpers for common time periods
|
|
3
4
|
*/
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.RANGE_PRESETS = void 0;
|
|
7
|
+
exports.today = today;
|
|
8
|
+
exports.yesterday = yesterday;
|
|
9
|
+
exports.tomorrow = tomorrow;
|
|
10
|
+
exports.lastNDays = lastNDays;
|
|
11
|
+
exports.nextNDays = nextNDays;
|
|
12
|
+
exports.thisWeek = thisWeek;
|
|
13
|
+
exports.lastWeek = lastWeek;
|
|
14
|
+
exports.nextWeek = nextWeek;
|
|
15
|
+
exports.thisMonth = thisMonth;
|
|
16
|
+
exports.lastMonth = lastMonth;
|
|
17
|
+
exports.nextMonth = nextMonth;
|
|
18
|
+
exports.thisYear = thisYear;
|
|
19
|
+
exports.lastYear = lastYear;
|
|
20
|
+
exports.nextYear = nextYear;
|
|
21
|
+
exports.rollingWindowDays = rollingWindowDays;
|
|
22
|
+
exports.quarterRange = quarterRange;
|
|
23
|
+
exports.lastQuarter = lastQuarter;
|
|
24
|
+
exports.nextQuarter = nextQuarter;
|
|
4
25
|
function todayRange(now = new Date()) {
|
|
5
26
|
const start = new Date(now);
|
|
6
27
|
start.setHours(0, 0, 0, 0);
|
|
@@ -8,33 +29,33 @@ function todayRange(now = new Date()) {
|
|
|
8
29
|
end.setDate(end.getDate() + 1);
|
|
9
30
|
return { start, end };
|
|
10
31
|
}
|
|
11
|
-
|
|
12
|
-
|
|
32
|
+
function today(now = new Date()) { return todayRange(now); }
|
|
33
|
+
function yesterday(now = new Date()) {
|
|
13
34
|
const t = todayRange(now);
|
|
14
35
|
const end = new Date(t.start); // yesterday end equals today start
|
|
15
36
|
const start = new Date(end);
|
|
16
37
|
start.setDate(start.getDate() - 1);
|
|
17
38
|
return { start, end };
|
|
18
39
|
}
|
|
19
|
-
|
|
40
|
+
function tomorrow(now = new Date()) {
|
|
20
41
|
const t = todayRange(now);
|
|
21
42
|
t.start.setDate(t.start.getDate() + 1);
|
|
22
43
|
t.end.setDate(t.end.getDate() + 1);
|
|
23
44
|
return t;
|
|
24
45
|
}
|
|
25
|
-
|
|
46
|
+
function lastNDays(n, now = new Date()) {
|
|
26
47
|
const end = new Date(now);
|
|
27
48
|
const start = new Date(end);
|
|
28
49
|
start.setDate(start.getDate() - n);
|
|
29
50
|
return { start, end };
|
|
30
51
|
}
|
|
31
|
-
|
|
52
|
+
function nextNDays(n, now = new Date()) {
|
|
32
53
|
const start = new Date(now);
|
|
33
54
|
const end = new Date(start);
|
|
34
55
|
end.setDate(end.getDate() + n);
|
|
35
56
|
return { start, end };
|
|
36
57
|
}
|
|
37
|
-
|
|
58
|
+
function thisWeek(now = new Date()) {
|
|
38
59
|
const start = new Date(now);
|
|
39
60
|
const day = start.getDay(); // 0 Sunday
|
|
40
61
|
start.setHours(0, 0, 0, 0);
|
|
@@ -43,78 +64,78 @@ export function thisWeek(now = new Date()) {
|
|
|
43
64
|
end.setDate(end.getDate() + 7);
|
|
44
65
|
return { start, end };
|
|
45
66
|
}
|
|
46
|
-
|
|
67
|
+
function lastWeek(now = new Date()) {
|
|
47
68
|
const w = thisWeek(now);
|
|
48
69
|
w.start.setDate(w.start.getDate() - 7);
|
|
49
70
|
w.end.setDate(w.end.getDate() - 7);
|
|
50
71
|
return w;
|
|
51
72
|
}
|
|
52
|
-
|
|
73
|
+
function nextWeek(now = new Date()) {
|
|
53
74
|
const w = thisWeek(now);
|
|
54
75
|
w.start.setDate(w.start.getDate() + 7);
|
|
55
76
|
w.end.setDate(w.end.getDate() + 7);
|
|
56
77
|
return w;
|
|
57
78
|
}
|
|
58
|
-
|
|
79
|
+
function thisMonth(now = new Date()) {
|
|
59
80
|
const start = new Date(now.getFullYear(), now.getMonth(), 1);
|
|
60
81
|
const end = new Date(now.getFullYear(), now.getMonth() + 1, 1);
|
|
61
82
|
return { start, end };
|
|
62
83
|
}
|
|
63
|
-
|
|
84
|
+
function lastMonth(now = new Date()) {
|
|
64
85
|
const m = thisMonth(now);
|
|
65
86
|
m.start.setMonth(m.start.getMonth() - 1);
|
|
66
87
|
m.end.setMonth(m.end.getMonth() - 1);
|
|
67
88
|
return m;
|
|
68
89
|
}
|
|
69
|
-
|
|
90
|
+
function nextMonth(now = new Date()) {
|
|
70
91
|
const m = thisMonth(now);
|
|
71
92
|
m.start.setMonth(m.start.getMonth() + 1);
|
|
72
93
|
m.end.setMonth(m.end.getMonth() + 1);
|
|
73
94
|
return m;
|
|
74
95
|
}
|
|
75
|
-
|
|
96
|
+
function thisYear(now = new Date()) {
|
|
76
97
|
const start = new Date(now.getFullYear(), 0, 1);
|
|
77
98
|
const end = new Date(now.getFullYear() + 1, 0, 1);
|
|
78
99
|
return { start, end };
|
|
79
100
|
}
|
|
80
|
-
|
|
101
|
+
function lastYear(now = new Date()) {
|
|
81
102
|
const y = thisYear(now);
|
|
82
103
|
y.start.setFullYear(y.start.getFullYear() - 1);
|
|
83
104
|
y.end.setFullYear(y.end.getFullYear() - 1);
|
|
84
105
|
return y;
|
|
85
106
|
}
|
|
86
|
-
|
|
107
|
+
function nextYear(now = new Date()) {
|
|
87
108
|
const y = thisYear(now);
|
|
88
109
|
y.start.setFullYear(y.start.getFullYear() + 1);
|
|
89
110
|
y.end.setFullYear(y.end.getFullYear() + 1);
|
|
90
111
|
return y;
|
|
91
112
|
}
|
|
92
|
-
|
|
113
|
+
function rollingWindowDays(days, now = new Date()) {
|
|
93
114
|
const end = new Date(now);
|
|
94
115
|
const start = new Date(end);
|
|
95
116
|
start.setDate(start.getDate() - days);
|
|
96
117
|
return { start, end };
|
|
97
118
|
}
|
|
98
|
-
|
|
119
|
+
function quarterRange(now = new Date()) {
|
|
99
120
|
const q = Math.floor(now.getMonth() / 3); // 0-3
|
|
100
121
|
const start = new Date(now.getFullYear(), q * 3, 1);
|
|
101
122
|
const end = new Date(now.getFullYear(), q * 3 + 3, 1);
|
|
102
123
|
return { start, end };
|
|
103
124
|
}
|
|
104
|
-
|
|
125
|
+
function lastQuarter(now = new Date()) {
|
|
105
126
|
const q = quarterRange(now);
|
|
106
127
|
q.start.setMonth(q.start.getMonth() - 3);
|
|
107
128
|
q.end.setMonth(q.end.getMonth() - 3);
|
|
108
129
|
return q;
|
|
109
130
|
}
|
|
110
|
-
|
|
131
|
+
function nextQuarter(now = new Date()) {
|
|
111
132
|
const q = quarterRange(now);
|
|
112
133
|
q.start.setMonth(q.start.getMonth() + 3);
|
|
113
134
|
q.end.setMonth(q.end.getMonth() + 3);
|
|
114
135
|
return q;
|
|
115
136
|
}
|
|
116
137
|
/** Map of preset functions for dynamic access */
|
|
117
|
-
|
|
138
|
+
exports.RANGE_PRESETS = {
|
|
118
139
|
today, yesterday, tomorrow,
|
|
119
140
|
last7Days: (now) => lastNDays(7, now),
|
|
120
141
|
last30Days: (now) => lastNDays(30, now),
|