diff --git a/spring-context/src/main/java/org/springframework/format/datetime/standard/DurationFormatterUtils.java b/spring-context/src/main/java/org/springframework/format/datetime/standard/DurationFormatterUtils.java index 00f95ed07dee..34372fe9af79 100644 --- a/spring-context/src/main/java/org/springframework/format/datetime/standard/DurationFormatterUtils.java +++ b/spring-context/src/main/java/org/springframework/format/datetime/standard/DurationFormatterUtils.java @@ -35,6 +35,7 @@ * @author Phillip Webb * @author Valentine Wu * @author Simon Baslé + * @author Kim Seungrae * @since 6.2 */ public abstract class DurationFormatterUtils { @@ -62,6 +63,7 @@ public static Duration parse(String value, DurationFormat.Style style) { * @return a duration */ public static Duration parse(String value, DurationFormat.Style style, @Nullable DurationFormat.Unit unit) { + Assert.hasText(value, () -> "Value must not be empty"); return switch (style) { case ISO8601 -> parseIso8601(value); case SIMPLE -> parseSimple(value, unit); @@ -88,6 +90,7 @@ public static String print(Duration value, DurationFormat.Style style) { * @return the printed result */ public static String print(Duration value, DurationFormat.Style style, @Nullable DurationFormat.Unit unit) { + Assert.notNull(value, "Value must not be null"); return switch (style) { case ISO8601 -> value.toString(); case SIMPLE -> printSimple(value, unit); @@ -149,7 +152,7 @@ private static Duration parseIso8601(String value) { try { return Duration.parse(value); } - catch (Throwable ex) { + catch (Exception ex) { throw new IllegalArgumentException("'" + value + "' is not a valid ISO-8601 duration", ex); } } diff --git a/spring-context/src/test/java/org/springframework/format/datetime/standard/DurationFormatterUtilsTests.java b/spring-context/src/test/java/org/springframework/format/datetime/standard/DurationFormatterUtilsTests.java index b1f78236a896..3174834e0cc4 100644 --- a/spring-context/src/test/java/org/springframework/format/datetime/standard/DurationFormatterUtilsTests.java +++ b/spring-context/src/test/java/org/springframework/format/datetime/standard/DurationFormatterUtilsTests.java @@ -23,7 +23,10 @@ import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.EnumSource; +import org.springframework.format.annotation.DurationFormat; import org.springframework.format.annotation.DurationFormat.Unit; import static org.assertj.core.api.Assertions.assertThat; @@ -188,6 +191,22 @@ void parseCompositeBadUnit() { .havingCause().withMessage("Does not match composite duration pattern"); } + @ParameterizedTest + @EnumSource(DurationFormat.Style.class) + void parseEmptyStringThrowsForAllStyles(DurationFormat.Style style) { + assertThatIllegalArgumentException() + .isThrownBy(() -> DurationFormatterUtils.parse("", style)) + .withMessage("Value must not be empty"); + } + + @ParameterizedTest + @EnumSource(DurationFormat.Style.class) + void parseNullStringThrowsForAllStyles(DurationFormat.Style style) { + assertThatIllegalArgumentException() + .isThrownBy(() -> DurationFormatterUtils.parse(null, style)) + .withMessage("Value must not be empty"); + } + @Test void printSimple() { assertThat(DurationFormatterUtils.print(Duration.ofNanos(12345), SIMPLE, Unit.NANOS)) @@ -240,6 +259,14 @@ void printCompositeNegative() { .isEqualTo("-1d2h34m57s28ms3us2ns"); } + @ParameterizedTest + @EnumSource(DurationFormat.Style.class) + void printNullDurationThrowsForAllStyles(DurationFormat.Style style) { + assertThatIllegalArgumentException() + .isThrownBy(() -> DurationFormatterUtils.print(null, style)) + .withMessage("Value must not be null"); + } + @Test void detectAndParse() { assertThat(DurationFormatterUtils.detectAndParse("PT1.234S", Unit.NANOS))