You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@flink.apache.org by ja...@apache.org on 2019/07/11 12:36:57 UTC
[flink] 01/02: [FLINK-13198][core] Add a utility to parse String to
Duration
This is an automated email from the ASF dual-hosted git repository.
jark pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/flink.git
commit 768e81f8c72837e41d6dd930308f188b2b827413
Author: TsReaper <ts...@gmail.com>
AuthorDate: Thu Jul 11 18:54:52 2019 +0800
[FLINK-13198][core] Add a utility to parse String to Duration
---
.../main/java/org/apache/flink/util/TimeUtils.java | 128 ++++++++++++++++++++
.../java/org/apache/flink/util/TimeUtilsTest.java | 131 +++++++++++++++++++++
2 files changed, 259 insertions(+)
diff --git a/flink-core/src/main/java/org/apache/flink/util/TimeUtils.java b/flink-core/src/main/java/org/apache/flink/util/TimeUtils.java
new file mode 100644
index 0000000..a9a8825
--- /dev/null
+++ b/flink-core/src/main/java/org/apache/flink/util/TimeUtils.java
@@ -0,0 +1,128 @@
+/*
+ * 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.flink.util;
+
+import java.time.Duration;
+import java.util.Locale;
+
+import static org.apache.flink.util.Preconditions.checkArgument;
+import static org.apache.flink.util.Preconditions.checkNotNull;
+
+/**
+ * Collection of utilities about time intervals.
+ */
+public class TimeUtils {
+
+ /**
+ * Parse the given string to a java {@link Duration}.
+ * The string is like "123ms", "321s", "12min" and such.
+ *
+ * @param text string to parse.
+ */
+ public static Duration parseDuration(String text) {
+ checkNotNull(text, "text");
+
+ final String trimmed = text.trim();
+ checkArgument(!trimmed.isEmpty(), "argument is an empty- or whitespace-only string");
+
+ final int len = trimmed.length();
+ int pos = 0;
+
+ char current;
+ while (pos < len && (current = trimmed.charAt(pos)) >= '0' && current <= '9') {
+ pos++;
+ }
+
+ final String number = trimmed.substring(0, pos);
+ final String unit = trimmed.substring(pos).trim().toLowerCase(Locale.US);
+
+ if (number.isEmpty()) {
+ throw new NumberFormatException("text does not start with a number");
+ }
+
+ final long value;
+ try {
+ value = Long.parseLong(number); // this throws a NumberFormatException on overflow
+ } catch (NumberFormatException e) {
+ throw new IllegalArgumentException("The value '" + number +
+ "' cannot be re represented as 64bit number (numeric overflow).");
+ }
+
+ final long multiplier;
+ if (unit.isEmpty()) {
+ multiplier = 1L;
+ } else {
+ if (matchTimeUnit(unit, TimeUnit.MILLISECONDS)) {
+ multiplier = 1L;
+ } else if (matchTimeUnit(unit, TimeUnit.SECONDS)) {
+ multiplier = 1000L;
+ } else if (matchTimeUnit(unit, TimeUnit.MINUTES)) {
+ multiplier = 1000L * 60L;
+ } else if (matchTimeUnit(unit, TimeUnit.HOURS)) {
+ multiplier = 1000L * 60L * 60L;
+ } else {
+ throw new IllegalArgumentException("Time interval unit '" + unit +
+ "' does not match any of the recognized units: " + TimeUnit.getAllUnits());
+ }
+ }
+
+ final long result = value * multiplier;
+
+ // check for overflow
+ if (result / multiplier != value) {
+ throw new IllegalArgumentException("The value '" + text +
+ "' cannot be re represented as 64bit number of bytes (numeric overflow).");
+ }
+
+ return Duration.ofMillis(result);
+ }
+
+ private static boolean matchTimeUnit(String text, TimeUnit unit) {
+ return text.equals(unit.getUnit());
+ }
+
+ /**
+ * Enum which defines time unit, mostly used to parse value from configuration file.
+ */
+ private enum TimeUnit {
+ MILLISECONDS("ms"),
+ SECONDS("s"),
+ MINUTES("min"),
+ HOURS("h");
+
+ private String unit;
+
+ TimeUnit(String unit) {
+ this.unit = unit;
+ }
+
+ public String getUnit() {
+ return unit;
+ }
+
+ public static String getAllUnits() {
+ return String.join(" | ", new String[]{
+ MILLISECONDS.getUnit(),
+ SECONDS.getUnit(),
+ MINUTES.getUnit(),
+ HOURS.getUnit()
+ });
+ }
+ }
+}
diff --git a/flink-core/src/test/java/org/apache/flink/util/TimeUtilsTest.java b/flink-core/src/test/java/org/apache/flink/util/TimeUtilsTest.java
new file mode 100644
index 0000000..632cf28
--- /dev/null
+++ b/flink-core/src/test/java/org/apache/flink/util/TimeUtilsTest.java
@@ -0,0 +1,131 @@
+/*
+ * 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.flink.util;
+
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+
+/**
+ * Tests for {@link TimeUtils}.
+ */
+public class TimeUtilsTest {
+
+ @Test
+ public void testParseDurationMillis() {
+ assertEquals(1234, TimeUtils.parseDuration("1234").toMillis());
+ assertEquals(1234, TimeUtils.parseDuration("1234ms").toMillis());
+ assertEquals(1234, TimeUtils.parseDuration("1234 ms").toMillis());
+ }
+
+ @Test
+ public void testParseDurationSeconds() {
+ assertEquals(667766, TimeUtils.parseDuration("667766s").getSeconds());
+ assertEquals(667766, TimeUtils.parseDuration("667766 s").getSeconds());
+ }
+
+ @Test
+ public void testParseDurationMinutes() {
+ assertEquals(7657623, TimeUtils.parseDuration("7657623min").toMinutes());
+ assertEquals(7657623, TimeUtils.parseDuration("7657623 min").toMinutes());
+ }
+
+ @Test
+ public void testParseDurationHours() {
+ assertEquals(987654, TimeUtils.parseDuration("987654h").toHours());
+ assertEquals(987654, TimeUtils.parseDuration("987654 h").toHours());
+ }
+
+ @Test
+ public void testParseDurationUpperCase() {
+ assertEquals(1L, TimeUtils.parseDuration("1 MS").toMillis());
+ assertEquals(1L, TimeUtils.parseDuration("1 S").getSeconds());
+ assertEquals(1L, TimeUtils.parseDuration("1 MIN").toMinutes());
+ assertEquals(1L, TimeUtils.parseDuration("1 H").toHours());
+ }
+
+ @Test
+ public void testParseDurationTrim() {
+ assertEquals(155L, TimeUtils.parseDuration(" 155 ").toMillis());
+ assertEquals(155L, TimeUtils.parseDuration(" 155 ms ").toMillis());
+ }
+
+ @Test
+ public void testParseDurationInvalid() {
+ // null
+ try {
+ TimeUtils.parseDuration(null);
+ fail("exception expected");
+ } catch (NullPointerException ignored) {
+ }
+
+ // empty
+ try {
+ TimeUtils.parseDuration("");
+ fail("exception expected");
+ } catch (IllegalArgumentException ignored) {
+ }
+
+ // blank
+ try {
+ TimeUtils.parseDuration(" ");
+ fail("exception expected");
+ } catch (IllegalArgumentException ignored) {
+ }
+
+ // no number
+ try {
+ TimeUtils.parseDuration("foobar or fubar or foo bazz");
+ fail("exception expected");
+ } catch (IllegalArgumentException ignored) {
+ }
+
+ // wrong unit
+ try {
+ TimeUtils.parseDuration("16 gjah");
+ fail("exception expected");
+ } catch (IllegalArgumentException ignored) {
+ }
+
+ // multiple numbers
+ try {
+ TimeUtils.parseDuration("16 16 17 18 ms");
+ fail("exception expected");
+ } catch (IllegalArgumentException ignored) {
+ }
+
+ // negative number
+ try {
+ TimeUtils.parseDuration("-100 ms");
+ fail("exception expected");
+ } catch (IllegalArgumentException ignored) {
+ }
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testParseDurationNumberOverflow() {
+ TimeUtils.parseDuration("100000000000000000000000000000000 ms");
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testParseDurationNumberTimeUnitOverflow() {
+ TimeUtils.parseDuration("100000000000000000 h");
+ }
+}