@zelgadis87/utils-core 5.3.3 → 5.3.4

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 CHANGED
@@ -2141,8 +2141,8 @@ class TimeInstant extends TimeBase {
2141
2141
  return timeInstantBuilder();
2142
2142
  }
2143
2143
  static fromIso8601(str) {
2144
- // Regex to capture: YYYY-MM-DDTHH:mm:ss.sssZ or YYYY-MM-DDTHH:mm:ss.sss±HH:mm
2145
- const iso8601Regex = /^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})\.(\d{3})(Z|[+-]\d{2}:\d{2})?$/;
2144
+ // Regex to capture: YYYY-MM-DDTHH:mm:ss[.sss]Z or YYYY-MM-DDTHH:mm:ss[.sss]±HH:mm
2145
+ const iso8601Regex = /^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})(?:\.(\d{3})|)(Z|[+-]\d{2}:\d{2})?$/;
2146
2146
  const match = str.match(iso8601Regex);
2147
2147
  if (!match) {
2148
2148
  throw new Error('Invalid ISO 8601 date format: ' + str);
@@ -2155,7 +2155,7 @@ class TimeInstant extends TimeBase {
2155
2155
  const hours = parseInt(hourStr, 10);
2156
2156
  const minutes = parseInt(minuteStr, 10);
2157
2157
  const seconds = parseInt(secondStr, 10);
2158
- const milliseconds = parseInt(millisecondStr, 10);
2158
+ const milliseconds = millisecondStr ? parseInt(millisecondStr, 10) : 0;
2159
2159
  // Validate each component using standalone validation functions
2160
2160
  if (!isValidYear(year))
2161
2161
  throw new Error('Invalid year in: ' + str);
@@ -2591,6 +2591,216 @@ function parseTimeInstantComponents(dateString, pattern, config = {}) {
2591
2591
  }
2592
2592
  return result;
2593
2593
  }
2594
+ /**
2595
+ * Parses a date string using only basic patterns that don't require Intl.
2596
+ * This is a safer alternative to parseTimeInstantComponents that works even when Intl is not available.
2597
+ *
2598
+ * @param dateString The date string to parse
2599
+ * @param pattern A basic pattern that doesn't require Intl (strongly typed)
2600
+ * @returns Partial time instant parameters that were parsed from the string
2601
+ * @throws Error if the string doesn't match the pattern or contains invalid values
2602
+ */
2603
+ function parseTimeInstantBasicComponents(dateString, pattern) {
2604
+ // Check if Intl is available, if so warn the user about the existing function
2605
+ const isIntlAvailable = typeof Intl !== 'undefined' && typeof Intl.DateTimeFormat !== 'undefined';
2606
+ if (isIntlAvailable)
2607
+ console.warn('Intl is available, use parseTimeInstantComponents instead of parseTimeInstantBasicComponents.');
2608
+ const result = {};
2609
+ let patternIndex = 0;
2610
+ let dateStringIndex = 0;
2611
+ // Helper function to check if a character is a pattern token
2612
+ const isPatternChar = (char) => ['y', 'M', 'd', 'H', 'm', 's', 'S'].includes(char);
2613
+ // Token extraction rules dictionary
2614
+ const tokenRules = {
2615
+ '': { maxLength: undefined }, // Default rule
2616
+ 'H': {
2617
+ maxLength: 2,
2618
+ validator: (value) => /^\d{1,2}$/.test(value) && parseInt(value, 10) >= 0 && parseInt(value, 10) <= 23,
2619
+ description: 'Hour (0-23, 1-2 digits)'
2620
+ },
2621
+ 'HH': {
2622
+ maxLength: 2,
2623
+ validator: (value) => /^\d{2}$/.test(value) && parseInt(value, 10) >= 0 && parseInt(value, 10) <= 23,
2624
+ description: 'Hour (00-23, exactly 2 digits)'
2625
+ },
2626
+ 'M': {
2627
+ maxLength: 2,
2628
+ validator: (value) => /^\d{1,2}$/.test(value) && parseInt(value, 10) >= 1 && parseInt(value, 10) <= 12,
2629
+ description: 'Month (1-12, 1-2 digits)'
2630
+ },
2631
+ 'MM': {
2632
+ maxLength: 2,
2633
+ validator: (value) => /^\d{2}$/.test(value) && parseInt(value, 10) >= 1 && parseInt(value, 10) <= 12,
2634
+ description: 'Month (01-12, exactly 2 digits)'
2635
+ },
2636
+ 'd': {
2637
+ maxLength: 2,
2638
+ validator: (value) => /^\d{1,2}$/.test(value) && parseInt(value, 10) >= 1 && parseInt(value, 10) <= 31,
2639
+ description: 'Day (1-31, 1-2 digits)'
2640
+ },
2641
+ 'dd': {
2642
+ maxLength: 2,
2643
+ validator: (value) => /^\d{2}$/.test(value) && parseInt(value, 10) >= 1 && parseInt(value, 10) <= 31,
2644
+ description: 'Day (01-31, exactly 2 digits)'
2645
+ },
2646
+ 'm': {
2647
+ maxLength: 2,
2648
+ validator: (value) => /^\d{1,2}$/.test(value) && parseInt(value, 10) >= 0 && parseInt(value, 10) <= 59,
2649
+ description: 'Minutes (0-59, 1-2 digits)'
2650
+ },
2651
+ 'mm': {
2652
+ maxLength: 2,
2653
+ validator: (value) => /^\d{2}$/.test(value) && parseInt(value, 10) >= 0 && parseInt(value, 10) <= 59,
2654
+ description: 'Minutes (00-59, exactly 2 digits)'
2655
+ },
2656
+ 's': {
2657
+ maxLength: 2,
2658
+ validator: (value) => /^\d{1,2}$/.test(value) && parseInt(value, 10) >= 0 && parseInt(value, 10) <= 59,
2659
+ description: 'Seconds (0-59, 1-2 digits)'
2660
+ },
2661
+ 'ss': {
2662
+ maxLength: 2,
2663
+ validator: (value) => /^\d{2}$/.test(value) && parseInt(value, 10) >= 0 && parseInt(value, 10) <= 59,
2664
+ description: 'Seconds (00-59, exactly 2 digits)'
2665
+ },
2666
+ 'S': {
2667
+ maxLength: 3,
2668
+ validator: (value) => /^\d{1,3}$/.test(value) && parseInt(value, 10) >= 0 && parseInt(value, 10) <= 999,
2669
+ description: 'Milliseconds (0-999, 1-3 digits)'
2670
+ },
2671
+ 'SS': {
2672
+ maxLength: 2,
2673
+ validator: (value) => /^\d{2}$/.test(value) && parseInt(value, 10) >= 0 && parseInt(value, 10) <= 99,
2674
+ description: 'Milliseconds (0-99, exactly 2 digits)'
2675
+ },
2676
+ 'SSS': {
2677
+ maxLength: 3,
2678
+ validator: (value) => /^\d{3}$/.test(value) && parseInt(value, 10) >= 0 && parseInt(value, 10) <= 999,
2679
+ description: 'Milliseconds (000-999, exactly 3 digits)'
2680
+ },
2681
+ 'y': {
2682
+ maxLength: 4,
2683
+ validator: (value) => /^\d{1,4}$/.test(value) && parseInt(value, 10) >= 0 && parseInt(value, 10) <= 9999,
2684
+ description: 'Year (0-9999, 1-4 digits)'
2685
+ },
2686
+ 'yy': {
2687
+ maxLength: 2,
2688
+ validator: (value) => /^\d{2}$/.test(value),
2689
+ description: 'Year (2 digits)'
2690
+ },
2691
+ 'yyyy': {
2692
+ maxLength: 4,
2693
+ validator: (value) => /^\d{4}$/.test(value),
2694
+ description: 'Year (4 digits)'
2695
+ }
2696
+ };
2697
+ // Helper function to extract a token value from the date string using token rules
2698
+ const extractValue = (tokenType, tokenLength) => {
2699
+ const tokenKey = tokenType.repeat(tokenLength);
2700
+ const rule = tokenRules[tokenKey] || tokenRules[''];
2701
+ // Extract consecutive digits up to maxLength
2702
+ let endIndex = dateStringIndex;
2703
+ let digitCount = 0;
2704
+ const maxChars = rule.maxLength || tokenLength;
2705
+ while (endIndex < dateString.length && /\d/.test(dateString[endIndex]) && digitCount < maxChars) {
2706
+ endIndex++;
2707
+ digitCount++;
2708
+ }
2709
+ const value = dateString.substring(dateStringIndex, endIndex);
2710
+ dateStringIndex = endIndex;
2711
+ // Validate the extracted value if a validator is provided
2712
+ if (rule.validator && !rule.validator(value)) {
2713
+ // Extract field name from description for better error messages
2714
+ const fieldName = (rule.description || tokenKey).split(' ')[0].toLowerCase();
2715
+ throw new Error(`Invalid ${fieldName} value: "${value}"`);
2716
+ }
2717
+ return value;
2718
+ };
2719
+ // Helper function to parse and validate a numeric value
2720
+ const parseNumericValue = (value, min, max, field) => {
2721
+ const num = parseInt(value, 10);
2722
+ if (isNaN(num) || num < min || num > max)
2723
+ throw new Error(`Invalid ${field} value: ${value}`);
2724
+ return num;
2725
+ };
2726
+ while (patternIndex < pattern.length) {
2727
+ const patternChar = pattern[patternIndex];
2728
+ if (isPatternChar(patternChar)) {
2729
+ // Start of a token - determine its full length
2730
+ let tokenLength = 0;
2731
+ while (patternIndex + tokenLength < pattern.length &&
2732
+ pattern[patternIndex + tokenLength] === patternChar) {
2733
+ tokenLength++;
2734
+ }
2735
+ // Extract and parse the corresponding value from the date string
2736
+ const value = extractValue(patternChar, tokenLength);
2737
+ // Check if we got enough characters for this token (after extraction)
2738
+ if (value.length < tokenLength) {
2739
+ throw new Error(`Date string "${dateString}" does not match pattern "${pattern}"`);
2740
+ }
2741
+ // Debug: log pattern processing (commented out for production)
2742
+ // console.log( `Processing patternChar="${patternChar}" with tokenLength=${tokenLength}, value="${value}"` );
2743
+ switch (patternChar) {
2744
+ case 'y': {
2745
+ if (tokenLength === 2) {
2746
+ // 2-digit year
2747
+ const shortYear = parseNumericValue(value, 0, 99, 'year');
2748
+ result.year = shortYear < 50 ? 2000 + shortYear : 1900 + shortYear;
2749
+ }
2750
+ else {
2751
+ // 4-digit year
2752
+ result.year = parseNumericValue(value, 0, 9999, 'year');
2753
+ }
2754
+ break;
2755
+ }
2756
+ case 'M':
2757
+ result.month = parseNumericValue(value, 1, 12, 'month');
2758
+ break;
2759
+ case 'd':
2760
+ result.date = parseNumericValue(value, 1, 31, 'day');
2761
+ break;
2762
+ case 'H':
2763
+ result.hours = parseNumericValue(value, 0, 23, 'hours');
2764
+ break;
2765
+ case 'm':
2766
+ result.minutes = parseNumericValue(value, 0, 59, 'minutes');
2767
+ break;
2768
+ case 's':
2769
+ result.seconds = parseNumericValue(value, 0, 59, 'seconds');
2770
+ break;
2771
+ case 'S': {
2772
+ let ms = parseInt(value, 10);
2773
+ if (isNaN(ms))
2774
+ throw new Error(`Invalid milliseconds value: ${value}`);
2775
+ // Normalize to milliseconds based on length
2776
+ if (tokenLength === 1)
2777
+ ms *= 100;
2778
+ else if (tokenLength === 2)
2779
+ ms *= 10;
2780
+ result.milliseconds = ms;
2781
+ break;
2782
+ }
2783
+ default:
2784
+ throw new Error(`Unsupported pattern character: ${patternChar}`);
2785
+ }
2786
+ patternIndex += tokenLength;
2787
+ }
2788
+ else {
2789
+ // Non-pattern character (separator like '-', '/', ':', ' ', '.')
2790
+ if (dateStringIndex >= dateString.length ||
2791
+ dateString[dateStringIndex] !== patternChar) {
2792
+ throw new Error(`Date string "${dateString}" does not match pattern "${pattern}"`);
2793
+ }
2794
+ patternIndex++;
2795
+ dateStringIndex++;
2796
+ }
2797
+ }
2798
+ // After processing the entire pattern, check if we consumed the entire date string
2799
+ if (dateStringIndex < dateString.length) {
2800
+ throw new Error(`Date string "${dateString}" does not match pattern "${pattern}"`);
2801
+ }
2802
+ return result;
2803
+ }
2594
2804
 
2595
2805
  const LEVELS = ["trace", "log", "debug", "info", "warn", "error"];
2596
2806
  const timestamp = () => TimeInstant.now().format('HH:mm:ss');
@@ -3404,6 +3614,8 @@ exports.pad = pad;
3404
3614
  exports.padLeft = padLeft;
3405
3615
  exports.padRight = padRight;
3406
3616
  exports.parseJson = parseJson;
3617
+ exports.parseTimeInstantBasicComponents = parseTimeInstantBasicComponents;
3618
+ exports.parseTimeInstantComponents = parseTimeInstantComponents;
3407
3619
  exports.partition = partition;
3408
3620
  exports.pick = pick;
3409
3621
  exports.pipedInvoke = pipedInvoke;