Polishing
	
		
			
	
		
	
	
		
			
				
	
				Backport Bot / build (push) Has been cancelled
				
					Details
				
			
		
			
				
	
				Build and Deploy Snapshot / Build and Deploy Snapshot (push) Has been cancelled
				
					Details
				
			
		
			
				
	
				CI / ${{ matrix.os.name}} | Java ${{ matrix.java.version}} (map[toolchain:false version:17], map[id:ubuntu-latest name:Linux]) (push) Has been cancelled
				
					Details
				
			
		
			
				
	
				CI / ${{ matrix.os.name}} | Java ${{ matrix.java.version}} (map[toolchain:true version:21], map[id:ubuntu-latest name:Linux]) (push) Has been cancelled
				
					Details
				
			
		
			
				
	
				CI / ${{ matrix.os.name}} | Java ${{ matrix.java.version}} (map[toolchain:true version:23], map[id:ubuntu-latest name:Linux]) (push) Has been cancelled
				
					Details
				
			
		
			
				
	
				Deploy Docs / Dispatch docs deployment (push) Has been cancelled
				
					Details
				
			
		
			
				
	
				Build and Deploy Snapshot / Verify (push) Has been cancelled
				
					Details
				
			
		
	
				
					
				
			
				
	
				Backport Bot / build (push) Has been cancelled
				
					Details
				
			
		
			
				
	
				Build and Deploy Snapshot / Build and Deploy Snapshot (push) Has been cancelled
				
					Details
				
			
		
			
				
	
				CI / ${{ matrix.os.name}} | Java ${{ matrix.java.version}} (map[toolchain:false version:17], map[id:ubuntu-latest name:Linux]) (push) Has been cancelled
				
					Details
				
			
		
			
				
	
				CI / ${{ matrix.os.name}} | Java ${{ matrix.java.version}} (map[toolchain:true version:21], map[id:ubuntu-latest name:Linux]) (push) Has been cancelled
				
					Details
				
			
		
			
				
	
				CI / ${{ matrix.os.name}} | Java ${{ matrix.java.version}} (map[toolchain:true version:23], map[id:ubuntu-latest name:Linux]) (push) Has been cancelled
				
					Details
				
			
		
			
				
	
				Deploy Docs / Dispatch docs deployment (push) Has been cancelled
				
					Details
				
			
		
			
				
	
				Build and Deploy Snapshot / Verify (push) Has been cancelled
				
					Details
				
			
		
	This commit is contained in:
		
							parent
							
								
									978590161a
								
							
						
					
					
						commit
						15364cf59f
					
				| 
						 | 
				
			
			@ -53,11 +53,19 @@ public @interface DurationFormat {
 | 
			
		|||
	 */
 | 
			
		||||
	Unit defaultUnit() default Unit.MILLIS;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * {@link Duration} format styles.
 | 
			
		||||
	 */
 | 
			
		||||
	enum Style {
 | 
			
		||||
 | 
			
		||||
		/**
 | 
			
		||||
		 * ISO-8601 formatting.
 | 
			
		||||
		 * <p>This is what the JDK uses in {@link Duration#parse(CharSequence)}
 | 
			
		||||
		 * and {@link Duration#toString()}.
 | 
			
		||||
		 */
 | 
			
		||||
		ISO8601,
 | 
			
		||||
 | 
			
		||||
		/**
 | 
			
		||||
		 * Simple formatting based on a short suffix, for example '1s'.
 | 
			
		||||
		 * <p>Supported unit suffixes include: {@code ns, us, ms, s, m, h, d}.
 | 
			
		||||
| 
						 | 
				
			
			@ -72,13 +80,6 @@ public @interface DurationFormat {
 | 
			
		|||
		 */
 | 
			
		||||
		SIMPLE,
 | 
			
		||||
 | 
			
		||||
		/**
 | 
			
		||||
		 * ISO-8601 formatting.
 | 
			
		||||
		 * <p>This is what the JDK uses in {@link Duration#parse(CharSequence)}
 | 
			
		||||
		 * and {@link Duration#toString()}.
 | 
			
		||||
		 */
 | 
			
		||||
		ISO8601,
 | 
			
		||||
 | 
			
		||||
		/**
 | 
			
		||||
		 * Like {@link #SIMPLE}, but allows multiple segments ordered from
 | 
			
		||||
		 * largest-to-smallest units of time, like {@code 1h12m27s}.
 | 
			
		||||
| 
						 | 
				
			
			@ -90,6 +91,7 @@ public @interface DurationFormat {
 | 
			
		|||
		COMPOSITE
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * {@link Duration} format unit, which mirrors a subset of {@link ChronoUnit} and
 | 
			
		||||
	 * allows conversion to and from a supported {@code ChronoUnit} as well as
 | 
			
		||||
| 
						 | 
				
			
			@ -227,7 +229,6 @@ public @interface DurationFormat {
 | 
			
		|||
			}
 | 
			
		||||
			throw new IllegalArgumentException("'" + suffix + "' is not a valid simple duration Unit");
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -28,6 +28,7 @@ import org.springframework.util.StringUtils;
 | 
			
		|||
/**
 | 
			
		||||
 * Support {@code Duration} parsing and printing in several styles, as listed in
 | 
			
		||||
 * {@link DurationFormat.Style}.
 | 
			
		||||
 *
 | 
			
		||||
 * <p>Some styles may not enforce any unit to be present, defaulting to {@code DurationFormat.Unit#MILLIS}
 | 
			
		||||
 * in that case. Methods in this class offer overloads that take a {@link DurationFormat.Unit} to
 | 
			
		||||
 * be used as a fall-back instead of the ultimate MILLIS default.
 | 
			
		||||
| 
						 | 
				
			
			@ -39,8 +40,38 @@ import org.springframework.util.StringUtils;
 | 
			
		|||
 */
 | 
			
		||||
public abstract class DurationFormatterUtils {
 | 
			
		||||
 | 
			
		||||
	private DurationFormatterUtils() {
 | 
			
		||||
		// singleton
 | 
			
		||||
	private static final Pattern ISO_8601_PATTERN = Pattern.compile("^[+-]?[pP].*$");
 | 
			
		||||
 | 
			
		||||
	private static final Pattern SIMPLE_PATTERN = Pattern.compile("^([+-]?\\d+)([a-zA-Z]{0,2})$");
 | 
			
		||||
 | 
			
		||||
	private static final Pattern COMPOSITE_PATTERN = Pattern.compile("^([+-]?)\\(?\\s?(\\d+d)?\\s?(\\d+h)?\\s?(\\d+m)?" +
 | 
			
		||||
			"\\s?(\\d+s)?\\s?(\\d+ms)?\\s?(\\d+us)?\\s?(\\d+ns)?\\)?$");
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Print the specified duration in the specified style.
 | 
			
		||||
	 * @param value the value to print
 | 
			
		||||
	 * @param style the style to print in
 | 
			
		||||
	 * @return the printed result
 | 
			
		||||
	 */
 | 
			
		||||
	public static String print(Duration value, DurationFormat.Style style) {
 | 
			
		||||
		return print(value, style, null);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Print the specified duration in the specified style using the given unit.
 | 
			
		||||
	 * @param value the value to print
 | 
			
		||||
	 * @param style the style to print in
 | 
			
		||||
	 * @param unit the unit to use for printing, if relevant ({@code null} will default
 | 
			
		||||
	 * to ms)
 | 
			
		||||
	 * @return the printed result
 | 
			
		||||
	 */
 | 
			
		||||
	public static String print(Duration value, DurationFormat.Style style, @Nullable DurationFormat.Unit unit) {
 | 
			
		||||
		return switch (style) {
 | 
			
		||||
			case ISO8601 -> value.toString();
 | 
			
		||||
			case SIMPLE -> printSimple(value, unit);
 | 
			
		||||
			case COMPOSITE -> printComposite(value);
 | 
			
		||||
		};
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
| 
						 | 
				
			
			@ -71,29 +102,24 @@ public abstract class DurationFormatterUtils {
 | 
			
		|||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Print the specified duration in the specified style.
 | 
			
		||||
	 * @param value the value to print
 | 
			
		||||
	 * @param style the style to print in
 | 
			
		||||
	 * @return the printed result
 | 
			
		||||
	 * Detect the style from the given source value.
 | 
			
		||||
	 * @param value the source value
 | 
			
		||||
	 * @return the duration style
 | 
			
		||||
	 * @throws IllegalArgumentException if the value is not a known style
 | 
			
		||||
	 */
 | 
			
		||||
	public static String print(Duration value, DurationFormat.Style style) {
 | 
			
		||||
		return print(value, style, null);
 | 
			
		||||
	public static DurationFormat.Style detect(String value) {
 | 
			
		||||
		Assert.notNull(value, "Value must not be null");
 | 
			
		||||
		// warning: the order of parsing starts to matter if multiple patterns accept a plain integer (no unit suffix)
 | 
			
		||||
		if (ISO_8601_PATTERN.matcher(value).matches()) {
 | 
			
		||||
			return DurationFormat.Style.ISO8601;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Print the specified duration in the specified style using the given unit.
 | 
			
		||||
	 * @param value the value to print
 | 
			
		||||
	 * @param style the style to print in
 | 
			
		||||
	 * @param unit the unit to use for printing, if relevant ({@code null} will default
 | 
			
		||||
	 * to ms)
 | 
			
		||||
	 * @return the printed result
 | 
			
		||||
	 */
 | 
			
		||||
	public static String print(Duration value, DurationFormat.Style style, @Nullable DurationFormat.Unit unit) {
 | 
			
		||||
		return switch (style) {
 | 
			
		||||
			case ISO8601 -> value.toString();
 | 
			
		||||
			case SIMPLE -> printSimple(value, unit);
 | 
			
		||||
			case COMPOSITE -> printComposite(value);
 | 
			
		||||
		};
 | 
			
		||||
		if (SIMPLE_PATTERN.matcher(value).matches()) {
 | 
			
		||||
			return DurationFormat.Style.SIMPLE;
 | 
			
		||||
		}
 | 
			
		||||
		if (COMPOSITE_PATTERN.matcher(value).matches()) {
 | 
			
		||||
			return DurationFormat.Style.COMPOSITE;
 | 
			
		||||
		}
 | 
			
		||||
		throw new IllegalArgumentException("'" + value + "' is not a valid duration, cannot detect any known style");
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
| 
						 | 
				
			
			@ -120,31 +146,6 @@ public abstract class DurationFormatterUtils {
 | 
			
		|||
		return parse(value, detect(value), unit);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Detect the style from the given source value.
 | 
			
		||||
	 * @param value the source value
 | 
			
		||||
	 * @return the duration style
 | 
			
		||||
	 * @throws IllegalArgumentException if the value is not a known style
 | 
			
		||||
	 */
 | 
			
		||||
	public static DurationFormat.Style detect(String value) {
 | 
			
		||||
		Assert.notNull(value, "Value must not be null");
 | 
			
		||||
		// warning: the order of parsing starts to matter if multiple patterns accept a plain integer (no unit suffix)
 | 
			
		||||
		if (ISO_8601_PATTERN.matcher(value).matches()) {
 | 
			
		||||
			return DurationFormat.Style.ISO8601;
 | 
			
		||||
		}
 | 
			
		||||
		if (SIMPLE_PATTERN.matcher(value).matches()) {
 | 
			
		||||
			return DurationFormat.Style.SIMPLE;
 | 
			
		||||
		}
 | 
			
		||||
		if (COMPOSITE_PATTERN.matcher(value).matches()) {
 | 
			
		||||
			return DurationFormat.Style.COMPOSITE;
 | 
			
		||||
		}
 | 
			
		||||
		throw new IllegalArgumentException("'" + value + "' is not a valid duration, cannot detect any known style");
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	private static final Pattern ISO_8601_PATTERN = Pattern.compile("^[+-]?[pP].*$");
 | 
			
		||||
	private static final Pattern SIMPLE_PATTERN = Pattern.compile("^([+-]?\\d+)([a-zA-Z]{0,2})$");
 | 
			
		||||
	private static final Pattern COMPOSITE_PATTERN = Pattern.compile("^([+-]?)\\(?\\s?(\\d+d)?\\s?(\\d+h)?\\s?(\\d+m)?" +
 | 
			
		||||
			"\\s?(\\d+s)?\\s?(\\d+ms)?\\s?(\\d+us)?\\s?(\\d+ns)?\\)?$");
 | 
			
		||||
 | 
			
		||||
	private static Duration parseIso8601(String value) {
 | 
			
		||||
		try {
 | 
			
		||||
| 
						 | 
				
			
			@ -155,6 +156,11 @@ public abstract class DurationFormatterUtils {
 | 
			
		|||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	private static String printSimple(Duration duration, @Nullable DurationFormat.Unit unit) {
 | 
			
		||||
		unit = (unit == null ? DurationFormat.Unit.MILLIS : unit);
 | 
			
		||||
		return unit.print(duration);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	private static Duration parseSimple(String text, @Nullable DurationFormat.Unit fallbackUnit) {
 | 
			
		||||
		try {
 | 
			
		||||
			Matcher matcher = SIMPLE_PATTERN.matcher(text);
 | 
			
		||||
| 
						 | 
				
			
			@ -171,34 +177,6 @@ public abstract class DurationFormatterUtils {
 | 
			
		|||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	private static String printSimple(Duration duration, @Nullable DurationFormat.Unit unit) {
 | 
			
		||||
		unit = (unit == null ? DurationFormat.Unit.MILLIS : unit);
 | 
			
		||||
		return unit.print(duration);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	private static Duration parseComposite(String text) {
 | 
			
		||||
		try {
 | 
			
		||||
			Matcher matcher = COMPOSITE_PATTERN.matcher(text);
 | 
			
		||||
			Assert.state(matcher.matches() && matcher.groupCount() > 1, "Does not match composite duration pattern");
 | 
			
		||||
			String sign = matcher.group(1);
 | 
			
		||||
			boolean negative = sign != null && sign.equals("-");
 | 
			
		||||
 | 
			
		||||
			Duration result = Duration.ZERO;
 | 
			
		||||
			DurationFormat.Unit[] units = DurationFormat.Unit.values();
 | 
			
		||||
			for (int i = 2; i < matcher.groupCount() + 1; i++) {
 | 
			
		||||
				String segment = matcher.group(i);
 | 
			
		||||
				if (StringUtils.hasText(segment)) {
 | 
			
		||||
					DurationFormat.Unit unit = units[units.length - i + 1];
 | 
			
		||||
					result = result.plus(unit.parse(segment.replace(unit.asSuffix(), "")));
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
			return negative ? result.negated() : result;
 | 
			
		||||
		}
 | 
			
		||||
		catch (Exception ex) {
 | 
			
		||||
			throw new IllegalArgumentException("'" + text + "' is not a valid composite duration", ex);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	private static String printComposite(Duration duration) {
 | 
			
		||||
		if (duration.isZero()) {
 | 
			
		||||
			return DurationFormat.Unit.SECONDS.print(duration);
 | 
			
		||||
| 
						 | 
				
			
			@ -243,4 +221,27 @@ public abstract class DurationFormatterUtils {
 | 
			
		|||
		return result.toString();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	private static Duration parseComposite(String text) {
 | 
			
		||||
		try {
 | 
			
		||||
			Matcher matcher = COMPOSITE_PATTERN.matcher(text);
 | 
			
		||||
			Assert.state(matcher.matches() && matcher.groupCount() > 1, "Does not match composite duration pattern");
 | 
			
		||||
			String sign = matcher.group(1);
 | 
			
		||||
			boolean negative = sign != null && sign.equals("-");
 | 
			
		||||
 | 
			
		||||
			Duration result = Duration.ZERO;
 | 
			
		||||
			DurationFormat.Unit[] units = DurationFormat.Unit.values();
 | 
			
		||||
			for (int i = 2; i < matcher.groupCount() + 1; i++) {
 | 
			
		||||
				String segment = matcher.group(i);
 | 
			
		||||
				if (StringUtils.hasText(segment)) {
 | 
			
		||||
					DurationFormat.Unit unit = units[units.length - i + 1];
 | 
			
		||||
					result = result.plus(unit.parse(segment.replace(unit.asSuffix(), "")));
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
			return negative ? result.negated() : result;
 | 
			
		||||
		}
 | 
			
		||||
		catch (Exception ex) {
 | 
			
		||||
			throw new IllegalArgumentException("'" + text + "' is not a valid composite duration", ex);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue