/*
 * 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.time.temporal.ChronoUnit;
import java.util.Collections;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;

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 {

	private static final Map<String, ChronoUnit> LABEL_TO_UNIT_MAP = Collections.unmodifiableMap(initMap());

	/**
	 * Parse the given string to a java {@link Duration}.
	 * The string is in format "{length value}{time unit label}", e.g. "123ms", "321 s".
	 * If no time unit label is specified, it will be considered as milliseconds.
	 *
	 * <p>Supported time unit labels are:
	 * <ul>
	 *     <li>DAYS： "d", "day"</li>
	 *     <li>HOURS： "h", "hour"</li>
	 *     <li>MINUTES： "min", "minute"</li>
	 *     <li>SECONDS： "s", "sec", "second"</li>
	 *     <li>MILLISECONDS： "ms", "milli", "millisecond"</li>
	 *     <li>MICROSECONDS： "µs", "micro", "microsecond"</li>
	 *     <li>NANOSECONDS： "ns", "nano", "nanosecond"</li>
	 * </ul>
	 *
	 * @param text string to parse.
	 */
	public static Duration parseDuration(String text) {
		checkNotNull(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 unitLabel = 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).");
		}

		if (unitLabel.isEmpty()) {
			return Duration.of(value, ChronoUnit.MILLIS);
		}

		ChronoUnit unit = LABEL_TO_UNIT_MAP.get(unitLabel);
		if (unit != null) {
			return Duration.of(value, unit);
		} else {
			throw new IllegalArgumentException("Time interval unit label '" + unitLabel +
				"' does not match any of the recognized units: " + TimeUnit.getAllUnits());
		}
	}

	private static Map<String, ChronoUnit> initMap() {
		Map<String, ChronoUnit> labelToUnit = new HashMap<>();
		for (TimeUnit timeUnit : TimeUnit.values()) {
			for (String label : timeUnit.getLabels()) {
				labelToUnit.put(label, timeUnit.getUnit());
			}
		}
		return labelToUnit;
	}

	/**
	 * Enum which defines time unit, mostly used to parse value from configuration file.
	 */
	private enum TimeUnit {

		DAYS(ChronoUnit.DAYS, "d", "day"),
		HOURS(ChronoUnit.HOURS, "h", "hour"),
		MINUTES(ChronoUnit.MINUTES, "min", "minute"),
		SECONDS(ChronoUnit.SECONDS, "s", "sec", "second"),
		MILLISECONDS(ChronoUnit.MILLIS, "ms", "milli", "millisecond"),
		MICROSECONDS(ChronoUnit.MICROS, "µs", "micro", "microsecond"),
		NANOSECONDS(ChronoUnit.NANOS, "ns", "nano", "nanosecond");

		private String[] labels;

		private ChronoUnit unit;

		TimeUnit(ChronoUnit unit, String... labels) {
			this.unit = unit;
			this.labels = labels;
		}

		public String[] getLabels() {
			return labels;
		}

		public ChronoUnit getUnit() {
			return unit;
		}

		public static String getAllUnits() {
			return String.join(", ", new String[]{
				"DAYS: (" + String.join(" | ", DAYS.getLabels()) + ")",
				"HOURS: (" + String.join(" | ", HOURS.getLabels()) + ")",
				"MINUTES: (" + String.join(" | ", MINUTES.getLabels()) + ")",
				"SECONDS: (" + String.join(" | ", SECONDS.getLabels()) + ")",
				"MILLISECONDS: (" + String.join(" | ", MILLISECONDS.getLabels()) + ")",
				"MICROSECONDS: (" + String.join(" | ", MICROSECONDS.getLabels()) + ")",
				"NANOSECONDS: (" + String.join(" | ", NANOSECONDS.getLabels()) + ")"
			});
		}
	}
}

@@ -897,16 +897,41 @@
 noUnit;%0A
+import java.util.Arrays;%0A
 import j
@@ -1024,16 +1024,52 @@
 til.Map;
+%0Aimport java.util.stream.Collectors;
 %0A%0Aimport
@@ -4413,462 +4413,222 @@
 urn 
-String.join(%22, %22, new String%5B%5D%7B%0A%09%09%09%09%22DAYS: (%22 + String.join(%22 %7C %22, DAYS.getLabels()) + %22)%22,%0A%09%09%09%09%22HOURS: (%22 + String.join(%22 %7C %22, HOURS.getLabels()) + %22)%22,%0A%09%09%09%09%22MINUTES: (%22 + String.join(%22 %7C %22, MINUTES.getLabels()) + %22)%22,%0A%09%09%09%09%22SECONDS: (%22 + String.join(%22 %7C %22, SECONDS.getLabels()) + %22)%22,%0A%09%09%09%09%22MILLISECONDS: (%22 + String.join(%22 %7C %22, MILLISECONDS.getLabels()) + %22)%22,%0A%09%09%09%09%22MICROSECONDS: (%22 + String.join(%22 %7C %22, MICROSECONDS.getLabels
+Arrays.stream(TimeUnit.values())%0A%09%09%09%09.map(TimeUnit::createTimeUnitString)%0A%09%09%09%09.collect(Collectors.joining(%22, %22));%0A%09%09%7D%0A%0A%09%09private static String createTimeUnitString(TimeUnit timeUnit) %7B%0A%09%09%09return timeUnit.name
 ()
-)
  + %22
-)%22,%0A%09%09%09%09%22NANOSECONDS
 : (%22
@@ -4649,27 +4649,24 @@
 (%22 %7C %22, 
-NANOSECONDS
+timeUnit
 .getLabe
@@ -4680,14 +4680,8 @@
  %22)%22
-%0A%09%09%09%7D)
 ;%0A%09%09

Streamline TimeUtils.TimeUnit.getAllUnits to automatically generate description