@zelgadis87/utils-core 5.3.3 → 5.3.5
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/.rollup/index.cjs +251 -5
- package/.rollup/index.cjs.map +1 -1
- package/.rollup/index.d.ts +45 -7
- package/.rollup/index.mjs +248 -6
- package/.rollup/index.mjs.map +1 -1
- package/.rollup/tsconfig.tsbuildinfo +1 -1
- package/CHANGELOG.md +15 -1
- package/package.json +7 -7
- package/src/time/TimeInstant.ts +373 -99
- package/src/utils/arrays/statistics.ts +16 -7
- package/src/utils/arrays.ts +30 -2
package/.rollup/index.cjs
CHANGED
|
@@ -447,9 +447,14 @@ function indexByWith(arr, keyGetter, valueMapper = v => v) {
|
|
|
447
447
|
}
|
|
448
448
|
|
|
449
449
|
function average(arr) {
|
|
450
|
+
if (arr.length === 0)
|
|
451
|
+
return null;
|
|
450
452
|
const f = 1 / arr.length;
|
|
451
453
|
return arr.reduce((tot, cur) => tot + (cur * f), 0);
|
|
452
454
|
}
|
|
455
|
+
function averageBy(arr, getter) {
|
|
456
|
+
return average(arr.map(getter));
|
|
457
|
+
}
|
|
453
458
|
function sum(arr) {
|
|
454
459
|
return arr.reduce((tot, cur) => tot + cur, 0);
|
|
455
460
|
}
|
|
@@ -458,7 +463,7 @@ function sumBy(arr, getter) {
|
|
|
458
463
|
}
|
|
459
464
|
function min(arr) {
|
|
460
465
|
if (arr.length === 0)
|
|
461
|
-
|
|
466
|
+
return null;
|
|
462
467
|
return arr.reduce((min, cur) => cur < min ? cur : min);
|
|
463
468
|
}
|
|
464
469
|
function minBy(arr, getter) {
|
|
@@ -466,7 +471,7 @@ function minBy(arr, getter) {
|
|
|
466
471
|
}
|
|
467
472
|
function max(arr) {
|
|
468
473
|
if (arr.length === 0)
|
|
469
|
-
|
|
474
|
+
return null;
|
|
470
475
|
return arr.reduce((max, cur) => cur > max ? cur : max);
|
|
471
476
|
}
|
|
472
477
|
function maxBy(arr, getter) {
|
|
@@ -621,9 +626,36 @@ function range(start, end) {
|
|
|
621
626
|
let length = (end - start) + 1;
|
|
622
627
|
return new Array(length).fill(1).map((_, i) => start + i);
|
|
623
628
|
}
|
|
629
|
+
/**
|
|
630
|
+
* Creates an array of the specified length, where each element is filled with the given value.
|
|
631
|
+
* @param length - The length of the array to create. Must be a non-negative integer.
|
|
632
|
+
* @param value - The value to fill each element with.
|
|
633
|
+
* @returns A new array with all elements set to the given value.
|
|
634
|
+
* @throws {RangeError} If length is negative, not an integer, or NaN.
|
|
635
|
+
* @example
|
|
636
|
+
* ```ts
|
|
637
|
+
* fill(3, 'a'); // ['a', 'a', 'a']
|
|
638
|
+
* fill(0, 42); // []
|
|
639
|
+
* fill(5, null); // [null, null, null, null, null]
|
|
640
|
+
* ```
|
|
641
|
+
*/
|
|
624
642
|
function fill(length, value) {
|
|
643
|
+
if (!Number.isInteger(length) || length < 0)
|
|
644
|
+
throw new RangeError(`Length must be a non-negative integer. Got: ${length}`);
|
|
625
645
|
return new Array(length).fill(value);
|
|
626
646
|
}
|
|
647
|
+
/**
|
|
648
|
+
* Creates an array of the specified length, where each element is generated by the provided generator function.
|
|
649
|
+
* @param length - The length of the array to create. Must be a non-negative integer.
|
|
650
|
+
* @param generator - A function that takes an index and returns the value for that position.
|
|
651
|
+
* @returns A new array with elements generated by the generator function.
|
|
652
|
+
* @throws {RangeError} If length is negative or not an integer.
|
|
653
|
+
*/
|
|
654
|
+
function fillWith(length, generator) {
|
|
655
|
+
if (!Number.isInteger(length) || length < 0)
|
|
656
|
+
throw new RangeError(`Length must be a non-negative integer. Got: ${length}`);
|
|
657
|
+
return Array.from({ length }, (_, i) => generator(i));
|
|
658
|
+
}
|
|
627
659
|
function extendArray(arr, props) {
|
|
628
660
|
return arr.map((t) => ({
|
|
629
661
|
...t,
|
|
@@ -2141,8 +2173,8 @@ class TimeInstant extends TimeBase {
|
|
|
2141
2173
|
return timeInstantBuilder();
|
|
2142
2174
|
}
|
|
2143
2175
|
static fromIso8601(str) {
|
|
2144
|
-
// Regex to capture: YYYY-MM-DDTHH:mm:ss.
|
|
2145
|
-
const iso8601Regex = /^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})
|
|
2176
|
+
// Regex to capture: YYYY-MM-DDTHH:mm:ss[.sss]Z or YYYY-MM-DDTHH:mm:ss[.sss]±HH:mm
|
|
2177
|
+
const iso8601Regex = /^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})(?:\.(\d{3})|)(Z|[+-]\d{2}:\d{2})?$/;
|
|
2146
2178
|
const match = str.match(iso8601Regex);
|
|
2147
2179
|
if (!match) {
|
|
2148
2180
|
throw new Error('Invalid ISO 8601 date format: ' + str);
|
|
@@ -2155,7 +2187,7 @@ class TimeInstant extends TimeBase {
|
|
|
2155
2187
|
const hours = parseInt(hourStr, 10);
|
|
2156
2188
|
const minutes = parseInt(minuteStr, 10);
|
|
2157
2189
|
const seconds = parseInt(secondStr, 10);
|
|
2158
|
-
const milliseconds = parseInt(millisecondStr, 10);
|
|
2190
|
+
const milliseconds = millisecondStr ? parseInt(millisecondStr, 10) : 0;
|
|
2159
2191
|
// Validate each component using standalone validation functions
|
|
2160
2192
|
if (!isValidYear(year))
|
|
2161
2193
|
throw new Error('Invalid year in: ' + str);
|
|
@@ -2591,6 +2623,216 @@ function parseTimeInstantComponents(dateString, pattern, config = {}) {
|
|
|
2591
2623
|
}
|
|
2592
2624
|
return result;
|
|
2593
2625
|
}
|
|
2626
|
+
/**
|
|
2627
|
+
* Parses a date string using only basic patterns that don't require Intl.
|
|
2628
|
+
* This is a safer alternative to parseTimeInstantComponents that works even when Intl is not available.
|
|
2629
|
+
*
|
|
2630
|
+
* @param dateString The date string to parse
|
|
2631
|
+
* @param pattern A basic pattern that doesn't require Intl (strongly typed)
|
|
2632
|
+
* @returns Partial time instant parameters that were parsed from the string
|
|
2633
|
+
* @throws Error if the string doesn't match the pattern or contains invalid values
|
|
2634
|
+
*/
|
|
2635
|
+
function parseTimeInstantBasicComponents(dateString, pattern, ignoreIntlAvailability = false) {
|
|
2636
|
+
// Check if Intl is available, if so warn the user about the existing function
|
|
2637
|
+
const isIntlAvailable = typeof Intl !== 'undefined' && typeof Intl.DateTimeFormat !== 'undefined';
|
|
2638
|
+
if (isIntlAvailable && !ignoreIntlAvailability)
|
|
2639
|
+
console.warn('Intl is available, use parseTimeInstantComponents instead of parseTimeInstantBasicComponents.');
|
|
2640
|
+
const result = {};
|
|
2641
|
+
let patternIndex = 0;
|
|
2642
|
+
let dateStringIndex = 0;
|
|
2643
|
+
// Helper function to check if a character is a pattern token
|
|
2644
|
+
const isPatternChar = (char) => ['y', 'M', 'd', 'H', 'm', 's', 'S'].includes(char);
|
|
2645
|
+
// Token extraction rules dictionary
|
|
2646
|
+
const tokenRules = {
|
|
2647
|
+
'': { maxLength: undefined }, // Default rule
|
|
2648
|
+
'H': {
|
|
2649
|
+
maxLength: 2,
|
|
2650
|
+
validator: (value) => /^\d{1,2}$/.test(value) && parseInt(value, 10) >= 0 && parseInt(value, 10) <= 23,
|
|
2651
|
+
description: 'Hour (0-23, 1-2 digits)'
|
|
2652
|
+
},
|
|
2653
|
+
'HH': {
|
|
2654
|
+
maxLength: 2,
|
|
2655
|
+
validator: (value) => /^\d{2}$/.test(value) && parseInt(value, 10) >= 0 && parseInt(value, 10) <= 23,
|
|
2656
|
+
description: 'Hour (00-23, exactly 2 digits)'
|
|
2657
|
+
},
|
|
2658
|
+
'M': {
|
|
2659
|
+
maxLength: 2,
|
|
2660
|
+
validator: (value) => /^\d{1,2}$/.test(value) && parseInt(value, 10) >= 1 && parseInt(value, 10) <= 12,
|
|
2661
|
+
description: 'Month (1-12, 1-2 digits)'
|
|
2662
|
+
},
|
|
2663
|
+
'MM': {
|
|
2664
|
+
maxLength: 2,
|
|
2665
|
+
validator: (value) => /^\d{2}$/.test(value) && parseInt(value, 10) >= 1 && parseInt(value, 10) <= 12,
|
|
2666
|
+
description: 'Month (01-12, exactly 2 digits)'
|
|
2667
|
+
},
|
|
2668
|
+
'd': {
|
|
2669
|
+
maxLength: 2,
|
|
2670
|
+
validator: (value) => /^\d{1,2}$/.test(value) && parseInt(value, 10) >= 1 && parseInt(value, 10) <= 31,
|
|
2671
|
+
description: 'Day (1-31, 1-2 digits)'
|
|
2672
|
+
},
|
|
2673
|
+
'dd': {
|
|
2674
|
+
maxLength: 2,
|
|
2675
|
+
validator: (value) => /^\d{2}$/.test(value) && parseInt(value, 10) >= 1 && parseInt(value, 10) <= 31,
|
|
2676
|
+
description: 'Day (01-31, exactly 2 digits)'
|
|
2677
|
+
},
|
|
2678
|
+
'm': {
|
|
2679
|
+
maxLength: 2,
|
|
2680
|
+
validator: (value) => /^\d{1,2}$/.test(value) && parseInt(value, 10) >= 0 && parseInt(value, 10) <= 59,
|
|
2681
|
+
description: 'Minutes (0-59, 1-2 digits)'
|
|
2682
|
+
},
|
|
2683
|
+
'mm': {
|
|
2684
|
+
maxLength: 2,
|
|
2685
|
+
validator: (value) => /^\d{2}$/.test(value) && parseInt(value, 10) >= 0 && parseInt(value, 10) <= 59,
|
|
2686
|
+
description: 'Minutes (00-59, exactly 2 digits)'
|
|
2687
|
+
},
|
|
2688
|
+
's': {
|
|
2689
|
+
maxLength: 2,
|
|
2690
|
+
validator: (value) => /^\d{1,2}$/.test(value) && parseInt(value, 10) >= 0 && parseInt(value, 10) <= 59,
|
|
2691
|
+
description: 'Seconds (0-59, 1-2 digits)'
|
|
2692
|
+
},
|
|
2693
|
+
'ss': {
|
|
2694
|
+
maxLength: 2,
|
|
2695
|
+
validator: (value) => /^\d{2}$/.test(value) && parseInt(value, 10) >= 0 && parseInt(value, 10) <= 59,
|
|
2696
|
+
description: 'Seconds (00-59, exactly 2 digits)'
|
|
2697
|
+
},
|
|
2698
|
+
'S': {
|
|
2699
|
+
maxLength: 3,
|
|
2700
|
+
validator: (value) => /^\d{1,3}$/.test(value) && parseInt(value, 10) >= 0 && parseInt(value, 10) <= 999,
|
|
2701
|
+
description: 'Milliseconds (0-999, 1-3 digits)'
|
|
2702
|
+
},
|
|
2703
|
+
'SS': {
|
|
2704
|
+
maxLength: 2,
|
|
2705
|
+
validator: (value) => /^\d{2}$/.test(value) && parseInt(value, 10) >= 0 && parseInt(value, 10) <= 99,
|
|
2706
|
+
description: 'Milliseconds (0-99, exactly 2 digits)'
|
|
2707
|
+
},
|
|
2708
|
+
'SSS': {
|
|
2709
|
+
maxLength: 3,
|
|
2710
|
+
validator: (value) => /^\d{3}$/.test(value) && parseInt(value, 10) >= 0 && parseInt(value, 10) <= 999,
|
|
2711
|
+
description: 'Milliseconds (000-999, exactly 3 digits)'
|
|
2712
|
+
},
|
|
2713
|
+
'y': {
|
|
2714
|
+
maxLength: 4,
|
|
2715
|
+
validator: (value) => /^\d{1,4}$/.test(value) && parseInt(value, 10) >= 0 && parseInt(value, 10) <= 9999,
|
|
2716
|
+
description: 'Year (0-9999, 1-4 digits)'
|
|
2717
|
+
},
|
|
2718
|
+
'yy': {
|
|
2719
|
+
maxLength: 2,
|
|
2720
|
+
validator: (value) => /^\d{2}$/.test(value),
|
|
2721
|
+
description: 'Year (2 digits)'
|
|
2722
|
+
},
|
|
2723
|
+
'yyyy': {
|
|
2724
|
+
maxLength: 4,
|
|
2725
|
+
validator: (value) => /^\d{4}$/.test(value),
|
|
2726
|
+
description: 'Year (4 digits)'
|
|
2727
|
+
}
|
|
2728
|
+
};
|
|
2729
|
+
// Helper function to extract a token value from the date string using token rules
|
|
2730
|
+
const extractValue = (tokenType, tokenLength) => {
|
|
2731
|
+
const tokenKey = tokenType.repeat(tokenLength);
|
|
2732
|
+
const rule = tokenRules[tokenKey] || tokenRules[''];
|
|
2733
|
+
// Extract consecutive digits up to maxLength
|
|
2734
|
+
let endIndex = dateStringIndex;
|
|
2735
|
+
let digitCount = 0;
|
|
2736
|
+
const maxChars = rule.maxLength || tokenLength;
|
|
2737
|
+
while (endIndex < dateString.length && /\d/.test(dateString[endIndex]) && digitCount < maxChars) {
|
|
2738
|
+
endIndex++;
|
|
2739
|
+
digitCount++;
|
|
2740
|
+
}
|
|
2741
|
+
const value = dateString.substring(dateStringIndex, endIndex);
|
|
2742
|
+
dateStringIndex = endIndex;
|
|
2743
|
+
// Validate the extracted value if a validator is provided
|
|
2744
|
+
if (rule.validator && !rule.validator(value)) {
|
|
2745
|
+
// Extract field name from description for better error messages
|
|
2746
|
+
const fieldName = (rule.description || tokenKey).split(' ')[0].toLowerCase();
|
|
2747
|
+
throw new Error(`Invalid ${fieldName} value: "${value}"`);
|
|
2748
|
+
}
|
|
2749
|
+
return value;
|
|
2750
|
+
};
|
|
2751
|
+
// Helper function to parse and validate a numeric value
|
|
2752
|
+
const parseNumericValue = (value, min, max, field) => {
|
|
2753
|
+
const num = parseInt(value, 10);
|
|
2754
|
+
if (isNaN(num) || num < min || num > max)
|
|
2755
|
+
throw new Error(`Invalid ${field} value: ${value}`);
|
|
2756
|
+
return num;
|
|
2757
|
+
};
|
|
2758
|
+
while (patternIndex < pattern.length) {
|
|
2759
|
+
const patternChar = pattern[patternIndex];
|
|
2760
|
+
if (isPatternChar(patternChar)) {
|
|
2761
|
+
// Start of a token - determine its full length
|
|
2762
|
+
let tokenLength = 0;
|
|
2763
|
+
while (patternIndex + tokenLength < pattern.length &&
|
|
2764
|
+
pattern[patternIndex + tokenLength] === patternChar) {
|
|
2765
|
+
tokenLength++;
|
|
2766
|
+
}
|
|
2767
|
+
// Extract and parse the corresponding value from the date string
|
|
2768
|
+
const value = extractValue(patternChar, tokenLength);
|
|
2769
|
+
// Check if we got enough characters for this token (after extraction)
|
|
2770
|
+
if (value.length < tokenLength) {
|
|
2771
|
+
throw new Error(`Date string "${dateString}" does not match pattern "${pattern}"`);
|
|
2772
|
+
}
|
|
2773
|
+
// Debug: log pattern processing (commented out for production)
|
|
2774
|
+
// console.log( `Processing patternChar="${patternChar}" with tokenLength=${tokenLength}, value="${value}"` );
|
|
2775
|
+
switch (patternChar) {
|
|
2776
|
+
case 'y': {
|
|
2777
|
+
if (tokenLength === 2) {
|
|
2778
|
+
// 2-digit year
|
|
2779
|
+
const shortYear = parseNumericValue(value, 0, 99, 'year');
|
|
2780
|
+
result.year = shortYear < 50 ? 2000 + shortYear : 1900 + shortYear;
|
|
2781
|
+
}
|
|
2782
|
+
else {
|
|
2783
|
+
// 4-digit year
|
|
2784
|
+
result.year = parseNumericValue(value, 0, 9999, 'year');
|
|
2785
|
+
}
|
|
2786
|
+
break;
|
|
2787
|
+
}
|
|
2788
|
+
case 'M':
|
|
2789
|
+
result.month = parseNumericValue(value, 1, 12, 'month');
|
|
2790
|
+
break;
|
|
2791
|
+
case 'd':
|
|
2792
|
+
result.date = parseNumericValue(value, 1, 31, 'day');
|
|
2793
|
+
break;
|
|
2794
|
+
case 'H':
|
|
2795
|
+
result.hours = parseNumericValue(value, 0, 23, 'hours');
|
|
2796
|
+
break;
|
|
2797
|
+
case 'm':
|
|
2798
|
+
result.minutes = parseNumericValue(value, 0, 59, 'minutes');
|
|
2799
|
+
break;
|
|
2800
|
+
case 's':
|
|
2801
|
+
result.seconds = parseNumericValue(value, 0, 59, 'seconds');
|
|
2802
|
+
break;
|
|
2803
|
+
case 'S': {
|
|
2804
|
+
let ms = parseInt(value, 10);
|
|
2805
|
+
if (isNaN(ms))
|
|
2806
|
+
throw new Error(`Invalid milliseconds value: ${value}`);
|
|
2807
|
+
// Normalize to milliseconds based on length
|
|
2808
|
+
if (tokenLength === 1)
|
|
2809
|
+
ms *= 100;
|
|
2810
|
+
else if (tokenLength === 2)
|
|
2811
|
+
ms *= 10;
|
|
2812
|
+
result.milliseconds = ms;
|
|
2813
|
+
break;
|
|
2814
|
+
}
|
|
2815
|
+
default:
|
|
2816
|
+
throw new Error(`Unsupported pattern character: ${patternChar}`);
|
|
2817
|
+
}
|
|
2818
|
+
patternIndex += tokenLength;
|
|
2819
|
+
}
|
|
2820
|
+
else {
|
|
2821
|
+
// Non-pattern character (separator like '-', '/', ':', ' ', '.')
|
|
2822
|
+
if (dateStringIndex >= dateString.length ||
|
|
2823
|
+
dateString[dateStringIndex] !== patternChar) {
|
|
2824
|
+
throw new Error(`Date string "${dateString}" does not match pattern "${pattern}"`);
|
|
2825
|
+
}
|
|
2826
|
+
patternIndex++;
|
|
2827
|
+
dateStringIndex++;
|
|
2828
|
+
}
|
|
2829
|
+
}
|
|
2830
|
+
// After processing the entire pattern, check if we consumed the entire date string
|
|
2831
|
+
if (dateStringIndex < dateString.length) {
|
|
2832
|
+
throw new Error(`Date string "${dateString}" does not match pattern "${pattern}"`);
|
|
2833
|
+
}
|
|
2834
|
+
return result;
|
|
2835
|
+
}
|
|
2594
2836
|
|
|
2595
2837
|
const LEVELS = ["trace", "log", "debug", "info", "warn", "error"];
|
|
2596
2838
|
const timestamp = () => TimeInstant.now().format('HH:mm:ss');
|
|
@@ -3300,6 +3542,7 @@ exports.arrayIncludes = arrayIncludes;
|
|
|
3300
3542
|
exports.asError = asError;
|
|
3301
3543
|
exports.asPromise = asPromise;
|
|
3302
3544
|
exports.average = average;
|
|
3545
|
+
exports.averageBy = averageBy;
|
|
3303
3546
|
exports.awaitAtMost = awaitAtMost;
|
|
3304
3547
|
exports.capitalizeWord = capitalizeWord;
|
|
3305
3548
|
exports.clamp = clamp;
|
|
@@ -3332,6 +3575,7 @@ exports.entriesToList = entriesToList;
|
|
|
3332
3575
|
exports.extendArray = extendArray;
|
|
3333
3576
|
exports.extendArrayWith = extendArrayWith;
|
|
3334
3577
|
exports.fill = fill;
|
|
3578
|
+
exports.fillWith = fillWith;
|
|
3335
3579
|
exports.filterMap = filterMap;
|
|
3336
3580
|
exports.filterMapReduce = filterMapReduce;
|
|
3337
3581
|
exports.filterWithTypePredicate = filterWithTypePredicate;
|
|
@@ -3404,6 +3648,8 @@ exports.pad = pad;
|
|
|
3404
3648
|
exports.padLeft = padLeft;
|
|
3405
3649
|
exports.padRight = padRight;
|
|
3406
3650
|
exports.parseJson = parseJson;
|
|
3651
|
+
exports.parseTimeInstantBasicComponents = parseTimeInstantBasicComponents;
|
|
3652
|
+
exports.parseTimeInstantComponents = parseTimeInstantComponents;
|
|
3407
3653
|
exports.partition = partition;
|
|
3408
3654
|
exports.pick = pick;
|
|
3409
3655
|
exports.pipedInvoke = pipedInvoke;
|