You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@camel.apache.org by da...@apache.org on 2020/10/30 13:46:51 UTC

[camel] 01/02: CAMEL-15761: camel-core - Optimize TimeUtils to calculate without regexp

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

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

commit d6743eea967823d08b845ac48eb21965561aafd2
Author: Claus Ibsen <cl...@gmail.com>
AuthorDate: Fri Oct 30 12:32:24 2020 +0100

    CAMEL-15761: camel-core - Optimize TimeUtils to calculate without regexp
---
 .../converter/TimePatternTypeConversionTest.java   |  16 +-
 .../main/java/org/apache/camel/util/TimeUtils.java | 224 +++++++++------------
 .../java/org/apache/camel/util/TimeUtilsTest.java  |  49 +++++
 .../ROOT/pages/camel-3x-upgrade-guide-3_7.adoc     |   8 +
 4 files changed, 161 insertions(+), 136 deletions(-)

diff --git a/core/camel-core/src/test/java/org/apache/camel/converter/TimePatternTypeConversionTest.java b/core/camel-core/src/test/java/org/apache/camel/converter/TimePatternTypeConversionTest.java
index 4e0be19..06d5c3c 100644
--- a/core/camel-core/src/test/java/org/apache/camel/converter/TimePatternTypeConversionTest.java
+++ b/core/camel-core/src/test/java/org/apache/camel/converter/TimePatternTypeConversionTest.java
@@ -38,29 +38,29 @@ public class TimePatternTypeConversionTest extends ContextTestSupport {
 
     @Test
     public void testHMSTimePattern() throws Exception {
-        long milliseconds = TimeUtils.toMilliSeconds("1hours30m1s");
+        long milliseconds = TimeUtils.toMilliSeconds("1h30m1s");
         assertEquals(5401000, milliseconds);
     }
 
     @Test
     public void testMTimePattern() throws Exception {
+        long milliseconds = TimeUtils.toMilliSeconds("5m");
+        assertEquals(300000, milliseconds);
+    }
+
+    @Test
+    public void testMandSTimePattern() throws Exception {
         long milliseconds = TimeUtils.toMilliSeconds("30m55s");
         assertEquals(1855000, milliseconds);
     }
 
     @Test
     public void testSecondsPattern() throws Exception {
-        long milliseconds = TimeUtils.toMilliSeconds("300 sec");
+        long milliseconds = TimeUtils.toMilliSeconds("300s");
         assertEquals(300000, milliseconds);
     }
 
     @Test
-    public void testDotSecPattern() throws Exception {
-        long milliseconds = TimeUtils.toMilliSeconds("0.300second");
-        assertEquals(300, milliseconds);
-    }
-
-    @Test
     public void testMillisPattern() throws Exception {
         long milliseconds = TimeUtils.toMilliSeconds("300ms");
         assertEquals(300, milliseconds);
diff --git a/core/camel-util/src/main/java/org/apache/camel/util/TimeUtils.java b/core/camel-util/src/main/java/org/apache/camel/util/TimeUtils.java
index 8917a87..aacd7c7 100644
--- a/core/camel-util/src/main/java/org/apache/camel/util/TimeUtils.java
+++ b/core/camel-util/src/main/java/org/apache/camel/util/TimeUtils.java
@@ -16,13 +16,7 @@
  */
 package org.apache.camel.util;
 
-import java.text.DecimalFormat;
-import java.text.DecimalFormatSymbols;
-import java.text.NumberFormat;
 import java.time.Duration;
-import java.util.Locale;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
 
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -33,15 +27,6 @@ import org.slf4j.LoggerFactory;
 public final class TimeUtils {
 
     private static final Logger LOG = LoggerFactory.getLogger(TimeUtils.class);
-    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))\\s*h(our(s)?)?(?=\\b|\\d|$)", Pattern.CASE_INSENSITIVE);
-    private static final Pattern MINUTES_REGEX_PATTERN
-            = Pattern.compile("((\\d)*(\\d))\\s*m(in(ute(s)?)?)?(?=\\b|\\d|$)", Pattern.CASE_INSENSITIVE);
-    private static final Pattern SECONDS_REGEX_PATTERN
-            = Pattern.compile("((\\d)(\\d)*)(\\.(\\d+))?\\s*s(ec(ond)?(s)?)?(?=\\b|\\d|$)", Pattern.CASE_INSENSITIVE);
-    private static final Pattern MILLIS_REGEX_PATTERN
-            = Pattern.compile("((\\d)(\\d)*)(\\.(\\d+))?\\s*m(illi)?s(ec(ond)?(s)?)?(?=\\b|\\d|$)", Pattern.CASE_INSENSITIVE);
 
     private TimeUtils() {
     }
@@ -51,7 +36,7 @@ public final class TimeUtils {
     }
 
     public static String printDuration(Duration uptime) {
-        return printDuration(uptime.toMillis());
+        return printDuration(uptime.toMillis(), true);
     }
 
     /**
@@ -60,41 +45,52 @@ public final class TimeUtils {
      * @param  uptime the uptime in millis
      * @return        the time used for displaying on screen or in logs
      */
-    public static String printDuration(double uptime) {
-        // Code taken from Karaf
-        // https://svn.apache.org/repos/asf/karaf/trunk/shell/commands/src/main/java/org/apache/karaf/shell/commands/impl/InfoAction.java
-
-        NumberFormat fmtI = new DecimalFormat("###,###", new DecimalFormatSymbols(Locale.ENGLISH));
-        NumberFormat fmtD = new DecimalFormat("###,##0.000", new DecimalFormatSymbols(Locale.ENGLISH));
+    public static String printDuration(long uptime) {
+        return printDuration(uptime, false);
+    }
 
-        uptime /= 1000;
-        if (uptime < 60) {
-            return fmtD.format(uptime) + " seconds";
-        }
-        uptime /= 60;
-        if (uptime < 60) {
-            long minutes = (long) uptime;
-            String s = fmtI.format(minutes) + (minutes > 1 ? " minutes" : " minute");
-            return s;
+    /**
+     * Prints the duration in a human readable format as X days Y hours Z minutes etc.
+     *
+     * @param  uptime  the uptime in millis
+     * @param  precise whether to be precise and include all details including milli seconds
+     * @return         the time used for displaying on screen or in logs
+     */
+    public static String printDuration(long uptime, boolean precise) {
+        StringBuilder sb = new StringBuilder();
+
+        long seconds = uptime / 1000;
+        long minutes = seconds / 60;
+        long hours = minutes / 60;
+        long days = hours / 24;
+        long millis = 0;
+        if (uptime > 1000) {
+            millis = uptime % 1000;
+        } else if (uptime < 1000) {
+            millis = uptime;
         }
-        uptime /= 60;
-        if (uptime < 24) {
-            long hours = (long) uptime;
-            long minutes = (long) ((uptime - hours) * 60);
-            String s = fmtI.format(hours) + (hours > 1 ? " hours" : " hour");
-            if (minutes != 0) {
-                s += " " + fmtI.format(minutes) + (minutes > 1 ? " minutes" : " minute");
-            }
-            return s;
+
+        if (days > 0) {
+            sb.append(days).append("d").append(hours % 24).append("h").append(minutes % 60).append("m").append(seconds % 60)
+                    .append("s");
+        } else if (hours > 0) {
+            sb.append(hours % 24).append("h").append(minutes % 60).append("m").append(seconds % 60).append("s");
+        } else if (minutes > 0) {
+            sb.append(minutes % 60).append("m").append(seconds % 60).append("s");
+        } else if (seconds > 0) {
+            sb.append(seconds % 60).append("s");
+            // lets include millis when there are only seconds by default
+            precise = true;
+        } else if (millis > 0) {
+            precise = false;
+            sb.append(millis).append("ms");
         }
-        uptime /= 24;
-        long days = (long) uptime;
-        long hours = (long) ((uptime - days) * 24);
-        String s = fmtI.format(days) + (days > 1 ? " days" : " day");
-        if (hours != 0) {
-            s += " " + fmtI.format(hours) + (hours > 1 ? " hours" : " hour");
+
+        if (precise & millis > 0) {
+            sb.append(millis).append("ms");
         }
-        return s;
+
+        return sb.toString();
     }
 
     public static Duration toDuration(String source) throws IllegalArgumentException {
@@ -120,101 +116,73 @@ public final class TimeUtils {
             return Long.parseLong(source);
         }
 
-        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));
-                foundFlag = true;
-                milliseconds = milliseconds + (60000 * minutes);
-            }
+        long days = 0;
+        long hours = 0;
+        long minutes = 0;
+        long seconds = 0;
+        long millis = 0;
+
+        int pos = source.indexOf('d');
+        if (pos != -1) {
+            String s = source.substring(0, pos);
+            days = Long.parseLong(s);
+            source = source.substring(pos + 1);
+        }
 
-            matcher = createMatcher(SECONDS_REGEX_PATTERN, source);
-            if (matcher.find()) {
-                long seconds = Long.parseLong(matcher.group(1));
-                milliseconds += 1000 * seconds;
-                if (matcher.group(5) != null && !matcher.group(5).isEmpty()) {
-                    long ms = Long.parseLong(matcher.group(5));
-                    milliseconds += ms;
-                }
-                foundFlag = true;
-            }
+        pos = source.indexOf('h');
+        if (pos != -1) {
+            String s = source.substring(0, pos);
+            hours = Long.parseLong(s);
+            source = source.substring(pos + 1);
+        }
 
-            matcher = createMatcher(MILLIS_REGEX_PATTERN, source);
-            if (matcher.find()) {
-                long millis = Long.parseLong(matcher.group(1));
-                foundFlag = true;
-                milliseconds += millis;
+        pos = source.indexOf('m');
+        if (pos != -1) {
+            boolean valid;
+            if (source.length() - 1 <= pos) {
+                valid = true;
+            } else {
+                // beware of minutes and not milli seconds
+                valid = source.charAt(pos + 1) != 's';
             }
-
-            // 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);
+            if (valid) {
+                String s = source.substring(0, pos);
+                minutes = Long.parseLong(s);
+                source = source.substring(pos + 1);
             }
         }
 
-        LOG.trace("source: [{}], milliseconds: {}", source, milliseconds);
-
-        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);
+        pos = source.indexOf('s');
+        // beware of seconds and not milli seconds
+        if (pos != -1 && source.charAt(pos - 1) != 'm') {
+            String s = source.substring(0, pos);
+            seconds = Long.parseLong(s);
+            source = source.substring(pos + 1);
         }
-        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);
+        pos = source.indexOf("ms");
+        if (pos != -1) {
+            String s = source.substring(0, pos);
+            millis = Long.parseLong(s);
         }
-        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);
+        long answer = millis;
+        if (seconds > 0) {
+            answer += 1000 * seconds;
         }
-        replaceSource = matcher.replaceFirst("");
-
-        //replace millis once
-        matcher = createMatcher(MILLIS_REGEX_PATTERN, replaceSource);
-        if (matcher.find() && matcher.find()) {
-            throw new IllegalArgumentException("Milliseconds should not be specified more then once: " + source);
+        if (minutes > 0) {
+            answer += 60000 * minutes;
         }
-        replaceSource = matcher.replaceFirst("");
-
-        if (replaceSource.length() > 0) {
-            throw new IllegalArgumentException("Illegal characters: " + source);
+        if (hours > 0) {
+            answer += 3600000 * hours;
         }
-    }
+        if (days > 0) {
+            answer += 86400000 * days;
+        }
+
+        LOG.trace("source: [{}], milliseconds: {}", source, answer);
 
-    private static Matcher createMatcher(Pattern pattern, String source) {
-        return pattern.matcher(source);
+        return answer;
     }
 
 }
diff --git a/core/camel-util/src/test/java/org/apache/camel/util/TimeUtilsTest.java b/core/camel-util/src/test/java/org/apache/camel/util/TimeUtilsTest.java
new file mode 100644
index 0000000..ebd7a8e
--- /dev/null
+++ b/core/camel-util/src/test/java/org/apache/camel/util/TimeUtilsTest.java
@@ -0,0 +1,49 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.util;
+
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+public class TimeUtilsTest {
+
+    @Test
+    public void testPrintDuration() throws Exception {
+        assertEquals("123ms", TimeUtils.printDuration(123));
+        assertEquals("1s250ms", TimeUtils.printDuration(1250));
+        assertEquals("33s", TimeUtils.printDuration(33000));
+        assertEquals("33s1ms", TimeUtils.printDuration(33001));
+        assertEquals("33s444ms", TimeUtils.printDuration(33444));
+        assertEquals("1m0s", TimeUtils.printDuration(60000));
+        assertEquals("1m1s", TimeUtils.printDuration(61000));
+        assertEquals("1m1s", TimeUtils.printDuration(61002));
+        assertEquals("1m1s2ms", TimeUtils.printDuration(61002, true));
+        assertEquals("30m55s", TimeUtils.printDuration(1855123));
+        assertEquals("30m55s123ms", TimeUtils.printDuration(1855123, true));
+        assertEquals("1h30m0s", TimeUtils.printDuration(5400000));
+        assertEquals("1h30m1s", TimeUtils.printDuration(5401000));
+        assertEquals("2d23h57m12s", TimeUtils.printDuration(259032000));
+    }
+
+    @Test
+    public void testReverse() throws Exception {
+        long time = 259032000;
+        long time2 = TimeUtils.toMilliSeconds(TimeUtils.printDuration(time));
+        assertEquals(time, time2);
+    }
+}
diff --git a/docs/user-manual/modules/ROOT/pages/camel-3x-upgrade-guide-3_7.adoc b/docs/user-manual/modules/ROOT/pages/camel-3x-upgrade-guide-3_7.adoc
index 16b8b66..16e0446 100644
--- a/docs/user-manual/modules/ROOT/pages/camel-3x-upgrade-guide-3_7.adoc
+++ b/docs/user-manual/modules/ROOT/pages/camel-3x-upgrade-guide-3_7.adoc
@@ -53,6 +53,14 @@ converting between two given types (also by walking up the parent classes and su
 But this leads to slower performance, and Camel now relies on there being covnerter methods with the exact combo
 of converting from/to types.
 
+==== Converting to milli seconds from text
+
+When converting to milli seconds using the shorthands for time precision with hours, minutes and seconds, then support
+for fractions is no longer supported. For example `delay=0.5m` (half minute) isn't supported instead use `delay=30s`.
+
+Support for using unites as `hours`, `minutes`, `seconds`, and `millis` has been removed.
+Units must now also be one of `h` for hours, `m` for minutes, `s` for seconds, and `ms` for millis (can be omitted).
+So you can use `1h12m37s42ms` for 1 hour, 12 minutes, 37 seconds and 42 milli seconds.
 
 === ProcessorFactory