@intech-software/chronera 0.1.6 → 0.1.7
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/dist/index.d.ts +4 -0
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -1
- package/dist/operations/age-countdown.d.ts +60 -0
- package/dist/operations/age-countdown.js +199 -0
- package/dist/operations/age-countdown.js.map +1 -0
- package/dist/operations/duration-ops.d.ts +43 -0
- package/dist/operations/duration-ops.js +393 -0
- package/dist/operations/duration-ops.js.map +1 -0
- package/dist/operations/interval.d.ts +55 -0
- package/dist/operations/interval.js +253 -0
- package/dist/operations/interval.js.map +1 -0
- package/dist/operations/natural-language.d.ts +13 -0
- package/dist/operations/natural-language.js +428 -0
- package/dist/operations/natural-language.js.map +1 -0
- package/dist/operations/recurrence.d.ts +48 -0
- package/dist/operations/recurrence.js +279 -0
- package/dist/operations/recurrence.js.map +1 -0
- package/dist/operations/time-series.d.ts +58 -0
- package/dist/operations/time-series.js +153 -0
- package/dist/operations/time-series.js.map +1 -0
- package/package.json +2 -2
|
@@ -0,0 +1,393 @@
|
|
|
1
|
+
import { ChroneraError } from "../errors/errors.js";
|
|
2
|
+
import { addDays, addMonths, addYears, getAbsoluteDay } from "./convenience.js";
|
|
3
|
+
// ---------------------------------------------------------------------------
|
|
4
|
+
// ISO 8601 Duration Parsing
|
|
5
|
+
// ---------------------------------------------------------------------------
|
|
6
|
+
/**
|
|
7
|
+
* Parses an ISO 8601 duration string into a `Duration` object.
|
|
8
|
+
* Examples: `'P1Y2M3DT4H5M6.789S'`, `'P1Y'`, `'-P1Y'`, `'P-1Y'`
|
|
9
|
+
*/
|
|
10
|
+
export function parseDuration(iso) {
|
|
11
|
+
if (!iso || typeof iso !== "string") {
|
|
12
|
+
throw new ChroneraError("CHRONERA_INVALID_DATE", `Invalid ISO 8601 duration string: ${JSON.stringify(iso)}`);
|
|
13
|
+
}
|
|
14
|
+
let str = iso.trim();
|
|
15
|
+
let sign = 1;
|
|
16
|
+
if (str.startsWith("-")) {
|
|
17
|
+
sign = -1;
|
|
18
|
+
str = str.slice(1);
|
|
19
|
+
}
|
|
20
|
+
else if (str.startsWith("+")) {
|
|
21
|
+
str = str.slice(1);
|
|
22
|
+
}
|
|
23
|
+
if (!str.startsWith("P")) {
|
|
24
|
+
throw new ChroneraError("CHRONERA_INVALID_DATE", `Invalid ISO 8601 duration string (must start with 'P'): ${JSON.stringify(iso)}`);
|
|
25
|
+
}
|
|
26
|
+
str = str.slice(1); // remove 'P'
|
|
27
|
+
// Split on 'T' to separate date and time parts
|
|
28
|
+
const tIdx = str.indexOf("T");
|
|
29
|
+
const datePart = tIdx === -1 ? str : str.slice(0, tIdx);
|
|
30
|
+
const timePart = tIdx === -1 ? "" : str.slice(tIdx + 1);
|
|
31
|
+
const result = {};
|
|
32
|
+
// Parse date part: [nY][nM][nW][nD]
|
|
33
|
+
// Each segment allows optional leading minus
|
|
34
|
+
const dateRegex = /(-?\d+(?:\.\d+)?)([YMWD])/g;
|
|
35
|
+
let match;
|
|
36
|
+
while ((match = dateRegex.exec(datePart)) !== null) {
|
|
37
|
+
const value = parseFloat(match[1]) * sign;
|
|
38
|
+
const unit = match[2];
|
|
39
|
+
switch (unit) {
|
|
40
|
+
case "Y":
|
|
41
|
+
result.years = Math.trunc(value);
|
|
42
|
+
break;
|
|
43
|
+
case "M":
|
|
44
|
+
result.months = Math.trunc(value);
|
|
45
|
+
break;
|
|
46
|
+
case "W":
|
|
47
|
+
result.weeks = Math.trunc(value);
|
|
48
|
+
break;
|
|
49
|
+
case "D":
|
|
50
|
+
result.days = Math.trunc(value);
|
|
51
|
+
break;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
// Parse time part: [nH][nM][n.nS]
|
|
55
|
+
const timeRegex = /(-?\d+(?:\.\d+)?)([HMS])/g;
|
|
56
|
+
while ((match = timeRegex.exec(timePart)) !== null) {
|
|
57
|
+
const value = parseFloat(match[1]) * sign;
|
|
58
|
+
const unit = match[2];
|
|
59
|
+
switch (unit) {
|
|
60
|
+
case "H":
|
|
61
|
+
result.hours = Math.trunc(value);
|
|
62
|
+
break;
|
|
63
|
+
case "M":
|
|
64
|
+
result.minutes = Math.trunc(value);
|
|
65
|
+
break;
|
|
66
|
+
case "S": {
|
|
67
|
+
const seconds = Math.trunc(value);
|
|
68
|
+
const ms = Math.round((Math.abs(value) - Math.abs(seconds)) * 1000) *
|
|
69
|
+
(value < 0 ? -1 : 1);
|
|
70
|
+
result.seconds = seconds;
|
|
71
|
+
if (ms !== 0)
|
|
72
|
+
result.milliseconds = ms;
|
|
73
|
+
break;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
// Validate: if nothing was parsed and string isn't just "P", error
|
|
78
|
+
if (datePart === "" && timePart === "" && Object.keys(result).length === 0) {
|
|
79
|
+
throw new ChroneraError("CHRONERA_INVALID_DATE", `Invalid ISO 8601 duration string (no components): ${JSON.stringify(iso)}`);
|
|
80
|
+
}
|
|
81
|
+
return result;
|
|
82
|
+
}
|
|
83
|
+
// ---------------------------------------------------------------------------
|
|
84
|
+
// ISO 8601 Duration Serialization
|
|
85
|
+
// ---------------------------------------------------------------------------
|
|
86
|
+
/**
|
|
87
|
+
* Converts a `Duration` object to an ISO 8601 duration string.
|
|
88
|
+
* Example: `{ years: 1, months: 2, days: 3 }` → `'P1Y2M3D'`
|
|
89
|
+
*/
|
|
90
|
+
export function durationToISO(dur) {
|
|
91
|
+
const years = dur.years ?? 0;
|
|
92
|
+
const months = dur.months ?? 0;
|
|
93
|
+
const weeks = dur.weeks ?? 0;
|
|
94
|
+
const days = dur.days ?? 0;
|
|
95
|
+
const hours = dur.hours ?? 0;
|
|
96
|
+
const minutes = dur.minutes ?? 0;
|
|
97
|
+
const seconds = dur.seconds ?? 0;
|
|
98
|
+
const ms = dur.milliseconds ?? 0;
|
|
99
|
+
let datePart = "P";
|
|
100
|
+
if (years !== 0)
|
|
101
|
+
datePart += `${years}Y`;
|
|
102
|
+
if (months !== 0)
|
|
103
|
+
datePart += `${months}M`;
|
|
104
|
+
if (weeks !== 0)
|
|
105
|
+
datePart += `${weeks}W`;
|
|
106
|
+
if (days !== 0)
|
|
107
|
+
datePart += `${days}D`;
|
|
108
|
+
let timePart = "";
|
|
109
|
+
if (hours !== 0)
|
|
110
|
+
timePart += `${hours}H`;
|
|
111
|
+
if (minutes !== 0)
|
|
112
|
+
timePart += `${minutes}M`;
|
|
113
|
+
if (seconds !== 0 || ms !== 0) {
|
|
114
|
+
if (ms !== 0) {
|
|
115
|
+
const totalSeconds = seconds + ms / 1000;
|
|
116
|
+
// Format fractional seconds: trim trailing zeros
|
|
117
|
+
const formatted = totalSeconds.toFixed(3).replace(/\.?0+$/, "");
|
|
118
|
+
timePart += `${formatted}S`;
|
|
119
|
+
}
|
|
120
|
+
else {
|
|
121
|
+
timePart += `${seconds}S`;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
const result = timePart ? `${datePart}T${timePart}` : datePart;
|
|
125
|
+
// "P" with nothing is valid for zero duration
|
|
126
|
+
return result;
|
|
127
|
+
}
|
|
128
|
+
const LOCALE_LABELS = {
|
|
129
|
+
en: {
|
|
130
|
+
years: ["year", "years"],
|
|
131
|
+
months: ["month", "months"],
|
|
132
|
+
weeks: ["week", "weeks"],
|
|
133
|
+
days: ["day", "days"],
|
|
134
|
+
hours: ["hour", "hours"],
|
|
135
|
+
minutes: ["minute", "minutes"],
|
|
136
|
+
seconds: ["second", "seconds"],
|
|
137
|
+
milliseconds: ["millisecond", "milliseconds"],
|
|
138
|
+
},
|
|
139
|
+
th: {
|
|
140
|
+
years: ["ปี", "ปี"],
|
|
141
|
+
months: ["เดือน", "เดือน"],
|
|
142
|
+
weeks: ["สัปดาห์", "สัปดาห์"],
|
|
143
|
+
days: ["วัน", "วัน"],
|
|
144
|
+
hours: ["ชั่วโมง", "ชั่วโมง"],
|
|
145
|
+
minutes: ["นาที", "นาที"],
|
|
146
|
+
seconds: ["วินาที", "วินาที"],
|
|
147
|
+
milliseconds: ["มิลลิวินาที", "มิลลิวินาที"],
|
|
148
|
+
},
|
|
149
|
+
ja: {
|
|
150
|
+
years: ["年", "年"],
|
|
151
|
+
months: ["ヶ月", "ヶ月"],
|
|
152
|
+
weeks: ["週間", "週間"],
|
|
153
|
+
days: ["日", "日"],
|
|
154
|
+
hours: ["時間", "時間"],
|
|
155
|
+
minutes: ["分", "分"],
|
|
156
|
+
seconds: ["秒", "秒"],
|
|
157
|
+
milliseconds: ["ミリ秒", "ミリ秒"],
|
|
158
|
+
},
|
|
159
|
+
zh: {
|
|
160
|
+
years: ["年", "年"],
|
|
161
|
+
months: ["个月", "个月"],
|
|
162
|
+
weeks: ["周", "周"],
|
|
163
|
+
days: ["天", "天"],
|
|
164
|
+
hours: ["小时", "小时"],
|
|
165
|
+
minutes: ["分钟", "分钟"],
|
|
166
|
+
seconds: ["秒", "秒"],
|
|
167
|
+
milliseconds: ["毫秒", "毫秒"],
|
|
168
|
+
},
|
|
169
|
+
ko: {
|
|
170
|
+
years: ["년", "년"],
|
|
171
|
+
months: ["개월", "개월"],
|
|
172
|
+
weeks: ["주", "주"],
|
|
173
|
+
days: ["일", "일"],
|
|
174
|
+
hours: ["시간", "시간"],
|
|
175
|
+
minutes: ["분", "분"],
|
|
176
|
+
seconds: ["초", "초"],
|
|
177
|
+
milliseconds: ["밀리초", "밀리초"],
|
|
178
|
+
},
|
|
179
|
+
fr: {
|
|
180
|
+
years: ["an", "ans"],
|
|
181
|
+
months: ["mois", "mois"],
|
|
182
|
+
weeks: ["semaine", "semaines"],
|
|
183
|
+
days: ["jour", "jours"],
|
|
184
|
+
hours: ["heure", "heures"],
|
|
185
|
+
minutes: ["minute", "minutes"],
|
|
186
|
+
seconds: ["seconde", "secondes"],
|
|
187
|
+
milliseconds: ["milliseconde", "millisecondes"],
|
|
188
|
+
},
|
|
189
|
+
de: {
|
|
190
|
+
years: ["Jahr", "Jahre"],
|
|
191
|
+
months: ["Monat", "Monate"],
|
|
192
|
+
weeks: ["Woche", "Wochen"],
|
|
193
|
+
days: ["Tag", "Tage"],
|
|
194
|
+
hours: ["Stunde", "Stunden"],
|
|
195
|
+
minutes: ["Minute", "Minuten"],
|
|
196
|
+
seconds: ["Sekunde", "Sekunden"],
|
|
197
|
+
milliseconds: ["Millisekunde", "Millisekunden"],
|
|
198
|
+
},
|
|
199
|
+
ar: {
|
|
200
|
+
years: ["سنة", "سنوات"],
|
|
201
|
+
months: ["شهر", "أشهر"],
|
|
202
|
+
weeks: ["أسبوع", "أسابيع"],
|
|
203
|
+
days: ["يوم", "أيام"],
|
|
204
|
+
hours: ["ساعة", "ساعات"],
|
|
205
|
+
minutes: ["دقيقة", "دقائق"],
|
|
206
|
+
seconds: ["ثانية", "ثوانٍ"],
|
|
207
|
+
milliseconds: ["ميلي ثانية", "ميلي ثانية"],
|
|
208
|
+
},
|
|
209
|
+
};
|
|
210
|
+
const DURATION_UNITS_ORDER = [
|
|
211
|
+
"years",
|
|
212
|
+
"months",
|
|
213
|
+
"weeks",
|
|
214
|
+
"days",
|
|
215
|
+
"hours",
|
|
216
|
+
"minutes",
|
|
217
|
+
"seconds",
|
|
218
|
+
"milliseconds",
|
|
219
|
+
];
|
|
220
|
+
const RTF_UNIT_MAP = {
|
|
221
|
+
years: "year",
|
|
222
|
+
months: "month",
|
|
223
|
+
weeks: "week",
|
|
224
|
+
days: "day",
|
|
225
|
+
hours: "hour",
|
|
226
|
+
minutes: "minute",
|
|
227
|
+
seconds: "second",
|
|
228
|
+
milliseconds: "second", // fallback
|
|
229
|
+
};
|
|
230
|
+
/**
|
|
231
|
+
* Returns a human-readable string for the duration in the given locale.
|
|
232
|
+
* Example (en): `{ days: 5, hours: 3, minutes: 30 }` → `'5 days, 3 hours, 30 minutes'`
|
|
233
|
+
*/
|
|
234
|
+
export function durationToHuman(dur, locale = "en") {
|
|
235
|
+
const langKey = (locale.split(/[-_]/)[0] ?? "en").toLowerCase();
|
|
236
|
+
const labels = LOCALE_LABELS[langKey];
|
|
237
|
+
const parts = [];
|
|
238
|
+
for (const unit of DURATION_UNITS_ORDER) {
|
|
239
|
+
const val = dur[unit];
|
|
240
|
+
if (val === undefined || val === 0)
|
|
241
|
+
continue;
|
|
242
|
+
const absVal = Math.abs(val);
|
|
243
|
+
if (labels) {
|
|
244
|
+
const [singular, plural] = labels[unit];
|
|
245
|
+
const label = absVal === 1 ? singular : plural;
|
|
246
|
+
// For Japanese / Chinese / Korean / Thai — no space between number and label
|
|
247
|
+
const noSpace = ["ja", "zh", "ko", "th"].includes(langKey);
|
|
248
|
+
parts.push(noSpace ? `${absVal}${label}` : `${absVal} ${label}`);
|
|
249
|
+
}
|
|
250
|
+
else {
|
|
251
|
+
// Fallback: use Intl.RelativeTimeFormat to get unit labels
|
|
252
|
+
try {
|
|
253
|
+
const rtf = new Intl.RelativeTimeFormat(locale, { numeric: "always" });
|
|
254
|
+
const rtfUnit = RTF_UNIT_MAP[unit];
|
|
255
|
+
const formatted = rtf.formatToParts(absVal, rtfUnit);
|
|
256
|
+
// Extract just the unit label part
|
|
257
|
+
const unitPart = formatted.find((p) => p.type === "unit");
|
|
258
|
+
const label = unitPart ? unitPart.value : unit;
|
|
259
|
+
parts.push(`${absVal} ${label}`);
|
|
260
|
+
}
|
|
261
|
+
catch {
|
|
262
|
+
parts.push(`${absVal} ${unit}`);
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
return (parts.join(", ") ||
|
|
267
|
+
"0 " + (LOCALE_LABELS["en"]?.["seconds"][1] ?? "seconds"));
|
|
268
|
+
}
|
|
269
|
+
// ---------------------------------------------------------------------------
|
|
270
|
+
// Duration Arithmetic on Dates
|
|
271
|
+
// ---------------------------------------------------------------------------
|
|
272
|
+
/**
|
|
273
|
+
* Adds a duration to a date (applies years, months, weeks, days in order).
|
|
274
|
+
* Time fields (hours, minutes, seconds, milliseconds) are ignored for date-only types.
|
|
275
|
+
*/
|
|
276
|
+
export function addDuration(date, dur) {
|
|
277
|
+
let result = date;
|
|
278
|
+
if (dur.years)
|
|
279
|
+
result = addYears(result, dur.years, "constrain");
|
|
280
|
+
if (dur.months)
|
|
281
|
+
result = addMonths(result, dur.months, "constrain");
|
|
282
|
+
const totalDays = (dur.weeks ?? 0) * 7 + (dur.days ?? 0);
|
|
283
|
+
if (totalDays)
|
|
284
|
+
result = addDays(result, totalDays);
|
|
285
|
+
return result;
|
|
286
|
+
}
|
|
287
|
+
/**
|
|
288
|
+
* Subtracts a duration from a date.
|
|
289
|
+
*/
|
|
290
|
+
export function subtractDuration(date, dur) {
|
|
291
|
+
return addDuration(date, scaleDuration(dur, -1));
|
|
292
|
+
}
|
|
293
|
+
// ---------------------------------------------------------------------------
|
|
294
|
+
// Diff as Duration
|
|
295
|
+
// ---------------------------------------------------------------------------
|
|
296
|
+
/**
|
|
297
|
+
* Computes the duration between two dates. Always returns positive field values.
|
|
298
|
+
* Returns `{ years, months, days }` representing the full decomposed diff.
|
|
299
|
+
*/
|
|
300
|
+
export function diffAsDuration(start, end) {
|
|
301
|
+
const startAbs = getAbsoluteDay(start);
|
|
302
|
+
const endAbs = getAbsoluteDay(end);
|
|
303
|
+
// Ensure start <= end for computation
|
|
304
|
+
let s = startAbs <= endAbs ? start : end;
|
|
305
|
+
let e = startAbs <= endAbs ? end : start;
|
|
306
|
+
// Count years
|
|
307
|
+
let years = 0;
|
|
308
|
+
let cursor = s;
|
|
309
|
+
while (true) {
|
|
310
|
+
const next = addYears(cursor, 1, "constrain");
|
|
311
|
+
if (getAbsoluteDay(next) > getAbsoluteDay(e))
|
|
312
|
+
break;
|
|
313
|
+
cursor = next;
|
|
314
|
+
years++;
|
|
315
|
+
}
|
|
316
|
+
// Count months
|
|
317
|
+
let months = 0;
|
|
318
|
+
while (true) {
|
|
319
|
+
const next = addMonths(cursor, 1, "constrain");
|
|
320
|
+
if (getAbsoluteDay(next) > getAbsoluteDay(e))
|
|
321
|
+
break;
|
|
322
|
+
cursor = next;
|
|
323
|
+
months++;
|
|
324
|
+
}
|
|
325
|
+
// Remaining days
|
|
326
|
+
const days = getAbsoluteDay(e) - getAbsoluteDay(cursor);
|
|
327
|
+
return {
|
|
328
|
+
...(years !== 0 ? { years } : {}),
|
|
329
|
+
...(months !== 0 ? { months } : {}),
|
|
330
|
+
...(days !== 0 ? { days } : {}),
|
|
331
|
+
};
|
|
332
|
+
}
|
|
333
|
+
// ---------------------------------------------------------------------------
|
|
334
|
+
// Duration Utilities
|
|
335
|
+
// ---------------------------------------------------------------------------
|
|
336
|
+
/**
|
|
337
|
+
* Sums two durations by adding corresponding fields.
|
|
338
|
+
*/
|
|
339
|
+
export function addDurations(a, b) {
|
|
340
|
+
const result = {};
|
|
341
|
+
const fields = [
|
|
342
|
+
"years",
|
|
343
|
+
"months",
|
|
344
|
+
"weeks",
|
|
345
|
+
"days",
|
|
346
|
+
"hours",
|
|
347
|
+
"minutes",
|
|
348
|
+
"seconds",
|
|
349
|
+
"milliseconds",
|
|
350
|
+
];
|
|
351
|
+
for (const f of fields) {
|
|
352
|
+
const sum = (a[f] ?? 0) + (b[f] ?? 0);
|
|
353
|
+
if (sum !== 0)
|
|
354
|
+
result[f] = sum;
|
|
355
|
+
}
|
|
356
|
+
return result;
|
|
357
|
+
}
|
|
358
|
+
/**
|
|
359
|
+
* Scales a duration by a numeric factor. Non-integer results are truncated.
|
|
360
|
+
*/
|
|
361
|
+
export function scaleDuration(dur, factor) {
|
|
362
|
+
const result = {};
|
|
363
|
+
const fields = [
|
|
364
|
+
"years",
|
|
365
|
+
"months",
|
|
366
|
+
"weeks",
|
|
367
|
+
"days",
|
|
368
|
+
"hours",
|
|
369
|
+
"minutes",
|
|
370
|
+
"seconds",
|
|
371
|
+
"milliseconds",
|
|
372
|
+
];
|
|
373
|
+
for (const f of fields) {
|
|
374
|
+
const val = dur[f];
|
|
375
|
+
if (val !== undefined) {
|
|
376
|
+
const scaled = Math.trunc(val * factor);
|
|
377
|
+
if (scaled !== 0)
|
|
378
|
+
result[f] = scaled;
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
return result;
|
|
382
|
+
}
|
|
383
|
+
/**
|
|
384
|
+
* Returns the approximate total number of days for a duration.
|
|
385
|
+
* Uses approximations: 1 year = 365 days, 1 month = 30 days.
|
|
386
|
+
*/
|
|
387
|
+
export function durationTotalDays(dur) {
|
|
388
|
+
return ((dur.years ?? 0) * 365 +
|
|
389
|
+
(dur.months ?? 0) * 30 +
|
|
390
|
+
(dur.weeks ?? 0) * 7 +
|
|
391
|
+
(dur.days ?? 0));
|
|
392
|
+
}
|
|
393
|
+
//# sourceMappingURL=duration-ops.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"duration-ops.js","sourceRoot":"","sources":["../../src/operations/duration-ops.ts"],"names":[],"mappings":"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","sourcesContent":["import { ChroneraError } from \"../errors/errors.js\";\nimport { addDays, addMonths, addYears, getAbsoluteDay } from \"./convenience.js\";\n\nimport type { DateOrCalendarDate, Duration } from \"../public-types.js\";\n\n// ---------------------------------------------------------------------------\n// ISO 8601 Duration Parsing\n// ---------------------------------------------------------------------------\n\n/**\n * Parses an ISO 8601 duration string into a `Duration` object.\n * Examples: `'P1Y2M3DT4H5M6.789S'`, `'P1Y'`, `'-P1Y'`, `'P-1Y'`\n */\nexport function parseDuration(iso: string): Duration {\n if (!iso || typeof iso !== \"string\") {\n throw new ChroneraError(\n \"CHRONERA_INVALID_DATE\",\n `Invalid ISO 8601 duration string: ${JSON.stringify(iso)}`,\n );\n }\n\n let str = iso.trim();\n let sign = 1;\n\n if (str.startsWith(\"-\")) {\n sign = -1;\n str = str.slice(1);\n } else if (str.startsWith(\"+\")) {\n str = str.slice(1);\n }\n\n if (!str.startsWith(\"P\")) {\n throw new ChroneraError(\n \"CHRONERA_INVALID_DATE\",\n `Invalid ISO 8601 duration string (must start with 'P'): ${JSON.stringify(iso)}`,\n );\n }\n\n str = str.slice(1); // remove 'P'\n\n // Split on 'T' to separate date and time parts\n const tIdx = str.indexOf(\"T\");\n const datePart = tIdx === -1 ? str : str.slice(0, tIdx);\n const timePart = tIdx === -1 ? \"\" : str.slice(tIdx + 1);\n\n const result: {\n years?: number;\n months?: number;\n weeks?: number;\n days?: number;\n hours?: number;\n minutes?: number;\n seconds?: number;\n milliseconds?: number;\n } = {};\n\n // Parse date part: [nY][nM][nW][nD]\n // Each segment allows optional leading minus\n const dateRegex = /(-?\\d+(?:\\.\\d+)?)([YMWD])/g;\n let match: RegExpExecArray | null;\n while ((match = dateRegex.exec(datePart)) !== null) {\n const value = parseFloat(match[1]!) * sign;\n const unit = match[2]!;\n switch (unit) {\n case \"Y\":\n result.years = Math.trunc(value);\n break;\n case \"M\":\n result.months = Math.trunc(value);\n break;\n case \"W\":\n result.weeks = Math.trunc(value);\n break;\n case \"D\":\n result.days = Math.trunc(value);\n break;\n }\n }\n\n // Parse time part: [nH][nM][n.nS]\n const timeRegex = /(-?\\d+(?:\\.\\d+)?)([HMS])/g;\n while ((match = timeRegex.exec(timePart)) !== null) {\n const value = parseFloat(match[1]!) * sign;\n const unit = match[2]!;\n switch (unit) {\n case \"H\":\n result.hours = Math.trunc(value);\n break;\n case \"M\":\n result.minutes = Math.trunc(value);\n break;\n case \"S\": {\n const seconds = Math.trunc(value);\n const ms =\n Math.round((Math.abs(value) - Math.abs(seconds)) * 1000) *\n (value < 0 ? -1 : 1);\n result.seconds = seconds;\n if (ms !== 0) result.milliseconds = ms;\n break;\n }\n }\n }\n\n // Validate: if nothing was parsed and string isn't just \"P\", error\n if (datePart === \"\" && timePart === \"\" && Object.keys(result).length === 0) {\n throw new ChroneraError(\n \"CHRONERA_INVALID_DATE\",\n `Invalid ISO 8601 duration string (no components): ${JSON.stringify(iso)}`,\n );\n }\n\n return result;\n}\n\n// ---------------------------------------------------------------------------\n// ISO 8601 Duration Serialization\n// ---------------------------------------------------------------------------\n\n/**\n * Converts a `Duration` object to an ISO 8601 duration string.\n * Example: `{ years: 1, months: 2, days: 3 }` → `'P1Y2M3D'`\n */\nexport function durationToISO(dur: Duration): string {\n const years = dur.years ?? 0;\n const months = dur.months ?? 0;\n const weeks = dur.weeks ?? 0;\n const days = dur.days ?? 0;\n const hours = dur.hours ?? 0;\n const minutes = dur.minutes ?? 0;\n const seconds = dur.seconds ?? 0;\n const ms = dur.milliseconds ?? 0;\n\n let datePart = \"P\";\n if (years !== 0) datePart += `${years}Y`;\n if (months !== 0) datePart += `${months}M`;\n if (weeks !== 0) datePart += `${weeks}W`;\n if (days !== 0) datePart += `${days}D`;\n\n let timePart = \"\";\n if (hours !== 0) timePart += `${hours}H`;\n if (minutes !== 0) timePart += `${minutes}M`;\n if (seconds !== 0 || ms !== 0) {\n if (ms !== 0) {\n const totalSeconds = seconds + ms / 1000;\n // Format fractional seconds: trim trailing zeros\n const formatted = totalSeconds.toFixed(3).replace(/\\.?0+$/, \"\");\n timePart += `${formatted}S`;\n } else {\n timePart += `${seconds}S`;\n }\n }\n\n const result = timePart ? `${datePart}T${timePart}` : datePart;\n // \"P\" with nothing is valid for zero duration\n return result;\n}\n\n// ---------------------------------------------------------------------------\n// Human-readable Duration\n// ---------------------------------------------------------------------------\n\ntype DurationUnit =\n | \"years\"\n | \"months\"\n | \"weeks\"\n | \"days\"\n | \"hours\"\n | \"minutes\"\n | \"seconds\"\n | \"milliseconds\";\n\nconst LOCALE_LABELS: Record<string, Record<DurationUnit, [string, string]>> = {\n en: {\n years: [\"year\", \"years\"],\n months: [\"month\", \"months\"],\n weeks: [\"week\", \"weeks\"],\n days: [\"day\", \"days\"],\n hours: [\"hour\", \"hours\"],\n minutes: [\"minute\", \"minutes\"],\n seconds: [\"second\", \"seconds\"],\n milliseconds: [\"millisecond\", \"milliseconds\"],\n },\n th: {\n years: [\"ปี\", \"ปี\"],\n months: [\"เดือน\", \"เดือน\"],\n weeks: [\"สัปดาห์\", \"สัปดาห์\"],\n days: [\"วัน\", \"วัน\"],\n hours: [\"ชั่วโมง\", \"ชั่วโมง\"],\n minutes: [\"นาที\", \"นาที\"],\n seconds: [\"วินาที\", \"วินาที\"],\n milliseconds: [\"มิลลิวินาที\", \"มิลลิวินาที\"],\n },\n ja: {\n years: [\"年\", \"年\"],\n months: [\"ヶ月\", \"ヶ月\"],\n weeks: [\"週間\", \"週間\"],\n days: [\"日\", \"日\"],\n hours: [\"時間\", \"時間\"],\n minutes: [\"分\", \"分\"],\n seconds: [\"秒\", \"秒\"],\n milliseconds: [\"ミリ秒\", \"ミリ秒\"],\n },\n zh: {\n years: [\"年\", \"年\"],\n months: [\"个月\", \"个月\"],\n weeks: [\"周\", \"周\"],\n days: [\"天\", \"天\"],\n hours: [\"小时\", \"小时\"],\n minutes: [\"分钟\", \"分钟\"],\n seconds: [\"秒\", \"秒\"],\n milliseconds: [\"毫秒\", \"毫秒\"],\n },\n ko: {\n years: [\"년\", \"년\"],\n months: [\"개월\", \"개월\"],\n weeks: [\"주\", \"주\"],\n days: [\"일\", \"일\"],\n hours: [\"시간\", \"시간\"],\n minutes: [\"분\", \"분\"],\n seconds: [\"초\", \"초\"],\n milliseconds: [\"밀리초\", \"밀리초\"],\n },\n fr: {\n years: [\"an\", \"ans\"],\n months: [\"mois\", \"mois\"],\n weeks: [\"semaine\", \"semaines\"],\n days: [\"jour\", \"jours\"],\n hours: [\"heure\", \"heures\"],\n minutes: [\"minute\", \"minutes\"],\n seconds: [\"seconde\", \"secondes\"],\n milliseconds: [\"milliseconde\", \"millisecondes\"],\n },\n de: {\n years: [\"Jahr\", \"Jahre\"],\n months: [\"Monat\", \"Monate\"],\n weeks: [\"Woche\", \"Wochen\"],\n days: [\"Tag\", \"Tage\"],\n hours: [\"Stunde\", \"Stunden\"],\n minutes: [\"Minute\", \"Minuten\"],\n seconds: [\"Sekunde\", \"Sekunden\"],\n milliseconds: [\"Millisekunde\", \"Millisekunden\"],\n },\n ar: {\n years: [\"سنة\", \"سنوات\"],\n months: [\"شهر\", \"أشهر\"],\n weeks: [\"أسبوع\", \"أسابيع\"],\n days: [\"يوم\", \"أيام\"],\n hours: [\"ساعة\", \"ساعات\"],\n minutes: [\"دقيقة\", \"دقائق\"],\n seconds: [\"ثانية\", \"ثوانٍ\"],\n milliseconds: [\"ميلي ثانية\", \"ميلي ثانية\"],\n },\n};\n\nconst DURATION_UNITS_ORDER: DurationUnit[] = [\n \"years\",\n \"months\",\n \"weeks\",\n \"days\",\n \"hours\",\n \"minutes\",\n \"seconds\",\n \"milliseconds\",\n];\n\nconst RTF_UNIT_MAP: Record<DurationUnit, Intl.RelativeTimeFormatUnit> = {\n years: \"year\",\n months: \"month\",\n weeks: \"week\",\n days: \"day\",\n hours: \"hour\",\n minutes: \"minute\",\n seconds: \"second\",\n milliseconds: \"second\", // fallback\n};\n\n/**\n * Returns a human-readable string for the duration in the given locale.\n * Example (en): `{ days: 5, hours: 3, minutes: 30 }` → `'5 days, 3 hours, 30 minutes'`\n */\nexport function durationToHuman(dur: Duration, locale: string = \"en\"): string {\n const langKey = (locale.split(/[-_]/)[0] ?? \"en\").toLowerCase();\n const labels = LOCALE_LABELS[langKey];\n\n const parts: string[] = [];\n\n for (const unit of DURATION_UNITS_ORDER) {\n const val = dur[unit];\n if (val === undefined || val === 0) continue;\n\n const absVal = Math.abs(val);\n\n if (labels) {\n const [singular, plural] = labels[unit];\n const label = absVal === 1 ? singular : plural;\n // For Japanese / Chinese / Korean / Thai — no space between number and label\n const noSpace = [\"ja\", \"zh\", \"ko\", \"th\"].includes(langKey);\n parts.push(noSpace ? `${absVal}${label}` : `${absVal} ${label}`);\n } else {\n // Fallback: use Intl.RelativeTimeFormat to get unit labels\n try {\n const rtf = new Intl.RelativeTimeFormat(locale, { numeric: \"always\" });\n const rtfUnit = RTF_UNIT_MAP[unit];\n const formatted = rtf.formatToParts(absVal, rtfUnit);\n // Extract just the unit label part\n const unitPart = formatted.find((p) => p.type === \"unit\");\n const label = unitPart ? unitPart.value : unit;\n parts.push(`${absVal} ${label}`);\n } catch {\n parts.push(`${absVal} ${unit}`);\n }\n }\n }\n\n return (\n parts.join(\", \") ||\n \"0 \" + (LOCALE_LABELS[\"en\"]?.[\"seconds\"][1] ?? \"seconds\")\n );\n}\n\n// ---------------------------------------------------------------------------\n// Duration Arithmetic on Dates\n// ---------------------------------------------------------------------------\n\n/**\n * Adds a duration to a date (applies years, months, weeks, days in order).\n * Time fields (hours, minutes, seconds, milliseconds) are ignored for date-only types.\n */\nexport function addDuration<T extends DateOrCalendarDate>(\n date: T,\n dur: Duration,\n): T {\n let result = date;\n if (dur.years) result = addYears(result, dur.years, \"constrain\");\n if (dur.months) result = addMonths(result, dur.months, \"constrain\");\n const totalDays = (dur.weeks ?? 0) * 7 + (dur.days ?? 0);\n if (totalDays) result = addDays(result, totalDays);\n return result;\n}\n\n/**\n * Subtracts a duration from a date.\n */\nexport function subtractDuration<T extends DateOrCalendarDate>(\n date: T,\n dur: Duration,\n): T {\n return addDuration(date, scaleDuration(dur, -1));\n}\n\n// ---------------------------------------------------------------------------\n// Diff as Duration\n// ---------------------------------------------------------------------------\n\n/**\n * Computes the duration between two dates. Always returns positive field values.\n * Returns `{ years, months, days }` representing the full decomposed diff.\n */\nexport function diffAsDuration(\n start: DateOrCalendarDate,\n end: DateOrCalendarDate,\n): Duration {\n const startAbs = getAbsoluteDay(start);\n const endAbs = getAbsoluteDay(end);\n\n // Ensure start <= end for computation\n let s = startAbs <= endAbs ? start : end;\n let e = startAbs <= endAbs ? end : start;\n\n // Count years\n let years = 0;\n let cursor = s;\n while (true) {\n const next = addYears(cursor, 1, \"constrain\");\n if (getAbsoluteDay(next) > getAbsoluteDay(e)) break;\n cursor = next;\n years++;\n }\n\n // Count months\n let months = 0;\n while (true) {\n const next = addMonths(cursor, 1, \"constrain\");\n if (getAbsoluteDay(next) > getAbsoluteDay(e)) break;\n cursor = next;\n months++;\n }\n\n // Remaining days\n const days = getAbsoluteDay(e) - getAbsoluteDay(cursor);\n\n return {\n ...(years !== 0 ? { years } : {}),\n ...(months !== 0 ? { months } : {}),\n ...(days !== 0 ? { days } : {}),\n };\n}\n\n// ---------------------------------------------------------------------------\n// Duration Utilities\n// ---------------------------------------------------------------------------\n\n/**\n * Sums two durations by adding corresponding fields.\n */\nexport function addDurations(a: Duration, b: Duration): Duration {\n const result: Record<string, number> = {};\n const fields: (keyof Duration)[] = [\n \"years\",\n \"months\",\n \"weeks\",\n \"days\",\n \"hours\",\n \"minutes\",\n \"seconds\",\n \"milliseconds\",\n ];\n for (const f of fields) {\n const sum = (a[f] ?? 0) + (b[f] ?? 0);\n if (sum !== 0) result[f] = sum;\n }\n return result as Duration;\n}\n\n/**\n * Scales a duration by a numeric factor. Non-integer results are truncated.\n */\nexport function scaleDuration(dur: Duration, factor: number): Duration {\n const result: Record<string, number> = {};\n const fields: (keyof Duration)[] = [\n \"years\",\n \"months\",\n \"weeks\",\n \"days\",\n \"hours\",\n \"minutes\",\n \"seconds\",\n \"milliseconds\",\n ];\n for (const f of fields) {\n const val = dur[f];\n if (val !== undefined) {\n const scaled = Math.trunc(val * factor);\n if (scaled !== 0) result[f] = scaled;\n }\n }\n return result as Duration;\n}\n\n/**\n * Returns the approximate total number of days for a duration.\n * Uses approximations: 1 year = 365 days, 1 month = 30 days.\n */\nexport function durationTotalDays(dur: Duration): number {\n return (\n (dur.years ?? 0) * 365 +\n (dur.months ?? 0) * 30 +\n (dur.weeks ?? 0) * 7 +\n (dur.days ?? 0)\n );\n}\n"]}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import type { DateOrCalendarDate, DateRange } from "../public-types.js";
|
|
2
|
+
/**
|
|
3
|
+
* Returns true if the given date falls within the range, respecting inclusivity flags.
|
|
4
|
+
*/
|
|
5
|
+
export declare function rangeContains<T extends DateOrCalendarDate>(range: DateRange<T>, date: T): boolean;
|
|
6
|
+
/**
|
|
7
|
+
* Returns true if two ranges have any overlapping dates (respecting inclusivity).
|
|
8
|
+
*/
|
|
9
|
+
export declare function rangeOverlaps<T extends DateOrCalendarDate>(a: DateRange<T>, b: DateRange<T>): boolean;
|
|
10
|
+
/**
|
|
11
|
+
* Returns the intersection of two ranges, or null if they do not overlap.
|
|
12
|
+
* The resulting range uses the most restrictive inclusivity at each boundary.
|
|
13
|
+
*/
|
|
14
|
+
export declare function rangeIntersection<T extends DateOrCalendarDate>(a: DateRange<T>, b: DateRange<T>): DateRange<T> | null;
|
|
15
|
+
/**
|
|
16
|
+
* Returns the union (bounding range) of two ranges.
|
|
17
|
+
* Throws if the ranges neither overlap nor are adjacent.
|
|
18
|
+
*/
|
|
19
|
+
export declare function rangeUnion<T extends DateOrCalendarDate>(a: DateRange<T>, b: DateRange<T>): DateRange<T>;
|
|
20
|
+
/**
|
|
21
|
+
* Returns the length of the range in days (absolute difference, respecting inclusivity).
|
|
22
|
+
* Exclusive boundaries reduce the count by one on the corresponding side.
|
|
23
|
+
*
|
|
24
|
+
* - `[s, e]` inclusive-inclusive → `e - s + 1`
|
|
25
|
+
* - `[s, e)` half-open → `e - s`
|
|
26
|
+
* - `(s, e]` half-open → `e - s`
|
|
27
|
+
* - `(s, e)` exclusive-exclusive → `e - s - 1`
|
|
28
|
+
*/
|
|
29
|
+
export declare function rangeLengthInDays<T extends DateOrCalendarDate>(range: DateRange<T>): number;
|
|
30
|
+
/**
|
|
31
|
+
* Returns an array of every day from start to end (both inclusive).
|
|
32
|
+
* Throws `ChroneraError('CHRONERA_OUT_OF_RANGE')` if the range exceeds 3650 days.
|
|
33
|
+
*/
|
|
34
|
+
export declare function eachDayOfInterval<T extends DateOrCalendarDate>(start: T, end: T): T[];
|
|
35
|
+
/**
|
|
36
|
+
* Returns an array of the first day of each 7-day week interval within [start, end].
|
|
37
|
+
*/
|
|
38
|
+
export declare function eachWeekOfInterval<T extends DateOrCalendarDate>(start: T, end: T): T[];
|
|
39
|
+
/**
|
|
40
|
+
* Returns the first day of each calendar month within [start, end].
|
|
41
|
+
*/
|
|
42
|
+
export declare function eachMonthOfInterval<T extends DateOrCalendarDate>(start: T, end: T): T[];
|
|
43
|
+
/**
|
|
44
|
+
* Splits a [start, end] interval into individual day ranges (each 1 day long).
|
|
45
|
+
*/
|
|
46
|
+
export declare function splitByDay<T extends DateOrCalendarDate>(start: T, end: T): DateRange<T>[];
|
|
47
|
+
/**
|
|
48
|
+
* Splits a [start, end] interval into week-long sub-ranges (7 days each),
|
|
49
|
+
* with the first and last sub-ranges bounded by the original start/end.
|
|
50
|
+
*/
|
|
51
|
+
export declare function splitByWeek<T extends DateOrCalendarDate>(start: T, end: T): DateRange<T>[];
|
|
52
|
+
/**
|
|
53
|
+
* Splits a [start, end] interval into month sub-ranges, bounded by original start/end.
|
|
54
|
+
*/
|
|
55
|
+
export declare function splitByMonth<T extends DateOrCalendarDate>(start: T, end: T): DateRange<T>[];
|