This is an automated email from the ASF dual-hosted git repository.

davsclaus pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/camel.git

commit 7423d2053e3660adb20d5e5702e98555b3b31e19
Author: Claus Ibsen <[email protected]>
AuthorDate: Tue Sep 29 09:14:01 2026 +0200

    camel-catalog - Validate durations with TimeUtils, the same parsing as the 
runtime
    
    The catalog kept its own copy of the old time pattern parser, so it rejected
    durations the runtime accepts (1d, 500ms, 1h 30m) and differed on edge 
cases.
    Validate with TimeUtils.toDuration, deprecate the catalog 
TimePatternConverter
    as a delegate to TimeUtils, and switch the camel-jbang since options to 
TimeUtils.
    
    Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
    Signed-off-by: Claus Ibsen <[email protected]>
---
 .../org/apache/camel/catalog/CamelCatalogTest.java |  9 +-
 .../camel/catalog/impl/AbstractCamelCatalog.java   | 28 ++-----
 .../camel/catalog/impl/TimePatternConverter.java   | 96 ++--------------------
 .../jbang/core/commands/action/CamelLogAction.java |  6 +-
 .../core/commands/action/CamelReceiveAction.java   |  5 +-
 .../core/commands/action/CamelTraceAction.java     |  5 +-
 6 files changed, 29 insertions(+), 120 deletions(-)

diff --git 
a/catalog/camel-catalog/src/test/java/org/apache/camel/catalog/CamelCatalogTest.java
 
b/catalog/camel-catalog/src/test/java/org/apache/camel/catalog/CamelCatalogTest.java
index 385601dd797d..e5bba5ad9f28 100644
--- 
a/catalog/camel-catalog/src/test/java/org/apache/camel/catalog/CamelCatalogTest.java
+++ 
b/catalog/camel-catalog/src/test/java/org/apache/camel/catalog/CamelCatalogTest.java
@@ -1090,7 +1090,14 @@ public class CamelCatalogTest {
         assertTrue(catalog.validateTimePattern("1h"));
         assertTrue(catalog.validateTimePattern("1hour"));
         assertTrue(catalog.validateTimePattern("2hours"));
-
+        // the same patterns as the runtime accepts
+        assertTrue(catalog.validateTimePattern("1d"));
+        assertTrue(catalog.validateTimePattern("500ms"));
+        assertTrue(catalog.validateTimePattern("1h 30m"));
+        assertTrue(catalog.validateTimePattern("PT5S"));
+
+        assertFalse(catalog.validateTimePattern(""));
+        assertFalse(catalog.validateTimePattern("5x"));
         assertFalse(catalog.validateTimePattern("bla"));
         assertFalse(catalog.validateTimePattern("2year"));
         assertFalse(catalog.validateTimePattern("60darn"));
diff --git 
a/core/camel-core-catalog/src/main/java/org/apache/camel/catalog/impl/AbstractCamelCatalog.java
 
b/core/camel-core-catalog/src/main/java/org/apache/camel/catalog/impl/AbstractCamelCatalog.java
index 5887612ab968..eecb0dcc4cb4 100644
--- 
a/core/camel-core-catalog/src/main/java/org/apache/camel/catalog/impl/AbstractCamelCatalog.java
+++ 
b/core/camel-core-catalog/src/main/java/org/apache/camel/catalog/impl/AbstractCamelCatalog.java
@@ -22,7 +22,6 @@ import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Method;
 import java.net.URI;
 import java.net.URISyntaxException;
-import java.time.Duration;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.HashMap;
@@ -64,6 +63,7 @@ import org.apache.camel.tooling.model.TransformerModel;
 import org.apache.camel.util.ObjectHelper;
 import org.apache.camel.util.ReflectionHelper;
 import org.apache.camel.util.StringHelper;
+import org.apache.camel.util.TimeUtils;
 import org.apache.camel.util.URISupport;
 
 import static org.apache.camel.util.StringHelper.isDashed;
@@ -1836,28 +1836,16 @@ public abstract class AbstractCamelCatalog {
     }
 
     private static boolean validateDuration(String value) {
-        boolean valid = false;
+        if (value == null || value.isBlank()) {
+            return false;
+        }
         try {
-            Long.parseLong(value);
-            valid = true;
+            // same parsing as the runtime: a number of millis, a time pattern 
such as 5s or 1h30m, or ISO-8601 (PT5S)
+            TimeUtils.toDuration(value);
+            return true;
         } catch (Exception e) {
-            // ignore
-        }
-        if (!valid) {
-            try {
-                if (value.startsWith("P") || value.startsWith("-P") || 
value.startsWith("p") || value.startsWith("-p")) {
-                    // its a duration
-                    Duration.parse(value);
-                } else {
-                    // it may be a time pattern, such as 5s for 5 seconds = 
5000
-                    TimePatternConverter.toMilliSeconds(value);
-                }
-                valid = true;
-            } catch (Exception e) {
-                // ignore
-            }
+            return false;
         }
-        return valid;
     }
 
     private static String stripOptionalPrefixFromName(Map<String, 
BaseOptionModel> rows, String name) {
diff --git 
a/core/camel-core-catalog/src/main/java/org/apache/camel/catalog/impl/TimePatternConverter.java
 
b/core/camel-core-catalog/src/main/java/org/apache/camel/catalog/impl/TimePatternConverter.java
index 420960baf3a0..801f3ace1c8d 100644
--- 
a/core/camel-core-catalog/src/main/java/org/apache/camel/catalog/impl/TimePatternConverter.java
+++ 
b/core/camel-core-catalog/src/main/java/org/apache/camel/catalog/impl/TimePatternConverter.java
@@ -16,19 +16,15 @@
  */
 package org.apache.camel.catalog.impl;
 
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
+import org.apache.camel.util.TimeUtils;
 
 /**
- * This class is a copy from camel-core so we can use it independent to 
validate uris with time patterns
+ * Converts a time pattern such as 5s or 1h30m to milliseconds.
+ *
+ * @deprecated use {@link TimeUtils#toMilliSeconds(String)} which is the same 
parsing as the runtime
  */
+@Deprecated(since = "4.23.0")
 public final class TimePatternConverter {
-    private static final Pattern NUMBERS_ONLY_STRING_PATTERN = 
Pattern.compile("^[-]?(\\d)+$", Pattern.CASE_INSENSITIVE);
-    private static final Pattern HOUR_REGEX_PATTERN = 
Pattern.compile("((\\d)*(\\d))h(our(s)?)?", Pattern.CASE_INSENSITIVE);
-    private static final Pattern MINUTES_REGEX_PATTERN
-            = Pattern.compile("((\\d)*(\\d))m(in(ute(s)?)?)?", 
Pattern.CASE_INSENSITIVE);
-    private static final Pattern SECONDS_REGEX_PATTERN
-            = Pattern.compile("((\\d)*(\\d))s(ec(ond)?(s)?)?", 
Pattern.CASE_INSENSITIVE);
 
     /**
      * Utility classes should not have a public constructor.
@@ -37,86 +33,6 @@ public final class TimePatternConverter {
     }
 
     public static long toMilliSeconds(String source) throws 
IllegalArgumentException {
-        long milliseconds = 0;
-        boolean foundFlag = false;
-
-        checkCorrectnessOfPattern(source);
-        Matcher matcher;
-
-        matcher = createMatcher(NUMBERS_ONLY_STRING_PATTERN, source);
-        if (matcher.find()) {
-            // Note: This will also be used for regular numeric strings.
-            //       This String -> long converter will be used for all 
strings.
-            milliseconds = Long.parseLong(source);
-        } else {
-            matcher = createMatcher(HOUR_REGEX_PATTERN, source);
-            if (matcher.find()) {
-                milliseconds = milliseconds + (3600000 * 
Long.parseLong(matcher.group(1)));
-                foundFlag = true;
-            }
-
-            matcher = createMatcher(MINUTES_REGEX_PATTERN, source);
-            if (matcher.find()) {
-                long minutes = Long.parseLong(matcher.group(1));
-                if (minutes > 59 && foundFlag) {
-                    throw new IllegalArgumentException("Minutes should contain 
a valid value between 0 and 59: " + source);
-                }
-                foundFlag = true;
-                milliseconds = milliseconds + (60000 * minutes);
-            }
-
-            matcher = createMatcher(SECONDS_REGEX_PATTERN, source);
-            if (matcher.find()) {
-                long seconds = Long.parseLong(matcher.group(1));
-                if (seconds > 59 && foundFlag) {
-                    throw new IllegalArgumentException("Seconds should contain 
a valid value between 0 and 59: " + source);
-                }
-                foundFlag = true;
-                milliseconds = milliseconds + (1000 * seconds);
-            }
-
-            // No pattern matched... initiating fallback check and conversion 
(if required).
-            // The source at this point may contain illegal values or special 
characters
-            if (!foundFlag) {
-                milliseconds = Long.parseLong(source);
-            }
-        }
-
-        return milliseconds;
-    }
-
-    private static void checkCorrectnessOfPattern(String source) {
-        //replace only numbers once
-        Matcher matcher = createMatcher(NUMBERS_ONLY_STRING_PATTERN, source);
-        String replaceSource = matcher.replaceFirst("");
-
-        //replace hour string once
-        matcher = createMatcher(HOUR_REGEX_PATTERN, replaceSource);
-        if (matcher.find() && matcher.find()) {
-            throw new IllegalArgumentException("Hours should not be specified 
more then once: " + source);
-        }
-        replaceSource = matcher.replaceFirst("");
-
-        //replace minutes once
-        matcher = createMatcher(MINUTES_REGEX_PATTERN, replaceSource);
-        if (matcher.find() && matcher.find()) {
-            throw new IllegalArgumentException("Minutes should not be 
specified more then once: " + source);
-        }
-        replaceSource = matcher.replaceFirst("");
-
-        //replace seconds once
-        matcher = createMatcher(SECONDS_REGEX_PATTERN, replaceSource);
-        if (matcher.find() && matcher.find()) {
-            throw new IllegalArgumentException("Seconds should not be 
specified more then once: " + source);
-        }
-        replaceSource = matcher.replaceFirst("");
-
-        if (!replaceSource.isEmpty()) {
-            throw new IllegalArgumentException("Illegal characters: " + 
source);
-        }
-    }
-
-    private static Matcher createMatcher(Pattern pattern, String source) {
-        return pattern.matcher(source);
+        return TimeUtils.toMilliSeconds(source);
     }
 }
diff --git 
a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/action/CamelLogAction.java
 
b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/action/CamelLogAction.java
index 07953aaf14f1..52c59799c3d8 100644
--- 
a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/action/CamelLogAction.java
+++ 
b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/action/CamelLogAction.java
@@ -37,7 +37,6 @@ import java.util.concurrent.ArrayBlockingQueue;
 import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.regex.Pattern;
 
-import org.apache.camel.catalog.impl.TimePatternConverter;
 import org.apache.camel.dsl.jbang.core.commands.CamelJBangMain;
 import org.apache.camel.dsl.jbang.core.commands.CommandHelper;
 import org.apache.camel.dsl.jbang.core.common.CommandLineHelper;
@@ -45,6 +44,7 @@ import 
org.apache.camel.dsl.jbang.core.common.EnvironmentHelper;
 import org.apache.camel.dsl.jbang.core.common.ProcessHelper;
 import org.apache.camel.util.StopWatch;
 import org.apache.camel.util.StringHelper;
+import org.apache.camel.util.TimeUtils;
 import org.apache.camel.util.json.JsonObject;
 import org.jline.jansi.Ansi;
 import org.jline.jansi.AnsiConsole;
@@ -157,9 +157,9 @@ public class CamelLogAction extends ActionBaseCommand {
                 long millis;
                 if (StringHelper.isDigit(since)) {
                     // is in seconds by default
-                    millis = TimePatternConverter.toMilliSeconds(since) * 1000;
+                    millis = TimeUtils.toMilliSeconds(since) * 1000;
                 } else {
-                    millis = TimePatternConverter.toMilliSeconds(since);
+                    millis = TimeUtils.toMilliSeconds(since);
                 }
                 limit = new Date(System.currentTimeMillis() - millis);
             }
diff --git 
a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/action/CamelReceiveAction.java
 
b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/action/CamelReceiveAction.java
index 57d5c16708cc..8541015558d4 100644
--- 
a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/action/CamelReceiveAction.java
+++ 
b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/action/CamelReceiveAction.java
@@ -42,7 +42,6 @@ import com.github.freva.asciitable.AsciiTable;
 import com.github.freva.asciitable.Column;
 import com.github.freva.asciitable.HorizontalAlign;
 import com.github.freva.asciitable.OverflowBehaviour;
-import org.apache.camel.catalog.impl.TimePatternConverter;
 import org.apache.camel.dsl.jbang.core.commands.CamelJBangMain;
 import org.apache.camel.dsl.jbang.core.commands.CommandHelper;
 import org.apache.camel.dsl.jbang.core.commands.MavenResolverMixin;
@@ -543,9 +542,9 @@ public class CamelReceiveAction extends ActionBaseCommand {
                 long millis;
                 if (StringHelper.isDigit(since)) {
                     // is in seconds by default
-                    millis = TimePatternConverter.toMilliSeconds(since) * 1000;
+                    millis = TimeUtils.toMilliSeconds(since) * 1000;
                 } else {
-                    millis = TimePatternConverter.toMilliSeconds(since);
+                    millis = TimeUtils.toMilliSeconds(since);
                 }
                 limit = new Date(System.currentTimeMillis() - millis);
             }
diff --git 
a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/action/CamelTraceAction.java
 
b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/action/CamelTraceAction.java
index 0c90cc4573c3..7af7bd991a8f 100644
--- 
a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/action/CamelTraceAction.java
+++ 
b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/action/CamelTraceAction.java
@@ -41,7 +41,6 @@ import com.github.freva.asciitable.AsciiTable;
 import com.github.freva.asciitable.Column;
 import com.github.freva.asciitable.HorizontalAlign;
 import com.github.freva.asciitable.OverflowBehaviour;
-import org.apache.camel.catalog.impl.TimePatternConverter;
 import org.apache.camel.dsl.jbang.core.commands.CamelJBangMain;
 import org.apache.camel.dsl.jbang.core.commands.CommandHelper;
 import org.apache.camel.dsl.jbang.core.common.PidNameAgeCompletionCandidates;
@@ -362,9 +361,9 @@ public class CamelTraceAction extends ActionBaseCommand {
                 long millis;
                 if (StringHelper.isDigit(since)) {
                     // is in seconds by default
-                    millis = TimePatternConverter.toMilliSeconds(since) * 1000;
+                    millis = TimeUtils.toMilliSeconds(since) * 1000;
                 } else {
-                    millis = TimePatternConverter.toMilliSeconds(since);
+                    millis = TimeUtils.toMilliSeconds(since);
                 }
                 limit = new Date(System.currentTimeMillis() - millis);
             }

Reply via email to