diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/format/DateTimeFormatters.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/format/DateTimeFormatters.java index 8941094224..7e2947d7c1 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/format/DateTimeFormatters.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/format/DateTimeFormatters.java @@ -43,8 +43,14 @@ public class DateTimeFormatters { * @return {@code this} for chained method invocation */ public DateTimeFormatters dateFormat(String pattern) { - this.dateFormatter = formatter(pattern); - this.datePattern = pattern; + if (isIso(pattern)) { + this.dateFormatter = DateTimeFormatter.ISO_LOCAL_DATE; + this.datePattern = "yyyy-MM-dd"; + } + else { + this.dateFormatter = formatter(pattern); + this.datePattern = pattern; + } return this; } @@ -54,7 +60,7 @@ public class DateTimeFormatters { * @return {@code this} for chained method invocation */ public DateTimeFormatters timeFormat(String pattern) { - this.timeFormatter = formatter(pattern); + this.timeFormatter = isIso(pattern) ? DateTimeFormatter.ISO_LOCAL_TIME : formatter(pattern); return this; } @@ -64,7 +70,7 @@ public class DateTimeFormatters { * @return {@code this} for chained method invocation */ public DateTimeFormatters dateTimeFormat(String pattern) { - this.dateTimeFormatter = formatter(pattern); + this.dateTimeFormatter = isIso(pattern) ? DateTimeFormatter.ISO_LOCAL_DATE_TIME : formatter(pattern); return this; } @@ -93,4 +99,8 @@ public class DateTimeFormatters { ? DateTimeFormatter.ofPattern(pattern).withResolverStyle(ResolverStyle.SMART) : null; } + private static boolean isIso(String pattern) { + return "iso".equalsIgnoreCase(pattern); + } + } diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/resources/META-INF/additional-spring-configuration-metadata.json b/spring-boot-project/spring-boot-autoconfigure/src/main/resources/META-INF/additional-spring-configuration-metadata.json index 4979e81188..9046fb6b13 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/resources/META-INF/additional-spring-configuration-metadata.json +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/resources/META-INF/additional-spring-configuration-metadata.json @@ -1938,6 +1938,114 @@ "name": "any" } ] + }, + { + "name": "spring.mvc.format.date", + "values": [ + { + "value": "dd/MM/yyyy", + "description": "Example date format. Any format supported by DateTimeFormatter.parse can be used." + }, + { + "value": "iso", + "description": "ISO-8601 extended local date format." + } + ], + "providers": [ + { + "name": "any" + } + ] + }, + { + "name": "spring.mvc.format.date-time", + "values": [ + { + "value": "yyyy-MM-dd HH:mm:ss", + "description": "Example date-time format. Any format supported by DateTimeFormatter.parse can be used." + }, + { + "value": "iso", + "description": "ISO-8601 extended local date-time format." + } + ], + "providers": [ + { + "name": "any" + } + ] + }, + { + "name": "spring.mvc.format.time", + "values": [ + { + "value": "HH:mm:ss", + "description": "Example time format. Any format supported by DateTimeFormatter.parse can be used." + }, + { + "value": "iso", + "description": "ISO-8601 extended local time format" + } + ], + "providers": [ + { + "name": "any" + } + ] + }, + { + "name": "spring.webflux.format.date", + "values": [ + { + "value": "dd/MM/yyyy", + "description": "Example date format. Any format supported by DateTimeFormatter.parse can be used." + }, + { + "value": "iso", + "description": "ISO-8601 extended local date format." + } + ], + "providers": [ + { + "name": "any" + } + ] + }, + { + "name": "spring.webflux.format.date-time", + "values": [ + { + "value": "yyyy-MM-dd HH:mm:ss", + "description": "Example date-time format. Any format supported by DateTimeFormatter.parse can be used." + }, + { + "value": "iso", + "description": "ISO-8601 extended local date-time format." + } + ], + "providers": [ + { + "name": "any" + } + ] + }, + { + "name": "spring.webflux.format.time", + "values": [ + { + "value": "HH:mm:ss", + "description": "Example time format. Any format supported by DateTimeFormatter.parse can be used." + }, + { + "value": "iso", + "description": "ISO-8601 extended local time format." + } + ], + "providers": [ + { + "name": "any" + } + ] } ] } diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/format/WebConversionServiceTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/format/WebConversionServiceTests.java index 8f5b81e53b..c78ee01f2c 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/format/WebConversionServiceTests.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/format/WebConversionServiceTests.java @@ -23,6 +23,7 @@ import java.time.ZoneId; import java.time.ZonedDateTime; import java.time.format.DateTimeFormatter; import java.time.format.FormatStyle; +import java.util.Calendar; import java.util.Date; import org.junit.jupiter.api.Test; @@ -45,6 +46,14 @@ class WebConversionServiceTests { .isEqualTo(DateTimeFormatter.ofLocalizedDate(FormatStyle.SHORT).format(date)); } + @Test + void isoDateFormat() { + WebConversionService conversionService = new WebConversionService(new DateTimeFormatters().dateFormat("iso")); + LocalDate date = LocalDate.of(2020, 4, 26); + assertThat(conversionService.convert(date, String.class)) + .isEqualTo(DateTimeFormatter.ISO_LOCAL_DATE.format(date)); + } + @Test void customDateFormatWithJavaUtilDate() { customDateFormat(Date.from(ZonedDateTime.of(2018, 1, 1, 20, 30, 0, 0, ZoneId.systemDefault()).toInstant())); @@ -63,6 +72,14 @@ class WebConversionServiceTests { .isEqualTo(DateTimeFormatter.ofLocalizedTime(FormatStyle.SHORT).format(time)); } + @Test + void isoTimeFormat() { + WebConversionService conversionService = new WebConversionService(new DateTimeFormatters().timeFormat("iso")); + LocalTime time = LocalTime.of(12, 45, 23); + assertThat(conversionService.convert(time, String.class)) + .isEqualTo(DateTimeFormatter.ISO_LOCAL_TIME.format(time)); + } + @Test void customTimeFormat() { WebConversionService conversionService = new WebConversionService( @@ -79,6 +96,15 @@ class WebConversionServiceTests { .isEqualTo(DateTimeFormatter.ofLocalizedDateTime(FormatStyle.SHORT).format(dateTime)); } + @Test + void isoDateTimeFormat() { + WebConversionService conversionService = new WebConversionService( + new DateTimeFormatters().dateTimeFormat("iso")); + LocalDateTime dateTime = LocalDateTime.of(2020, 4, 26, 12, 45, 23); + assertThat(conversionService.convert(dateTime, String.class)) + .isEqualTo(DateTimeFormatter.ISO_LOCAL_DATE_TIME.format(dateTime)); + } + @Test void customDateTimeFormat() { WebConversionService conversionService = new WebConversionService( @@ -88,13 +114,31 @@ class WebConversionServiceTests { } @Test - void convertFromStringToDate() { + void convertFromStringToLocalDate() { WebConversionService conversionService = new WebConversionService( new DateTimeFormatters().dateFormat("yyyy-MM-dd")); - java.time.LocalDate date = conversionService.convert("2018-01-01", java.time.LocalDate.class); + LocalDate date = conversionService.convert("2018-01-01", LocalDate.class); assertThat(date).isEqualTo(java.time.LocalDate.of(2018, 1, 1)); } + @Test + void convertFromStringToLocalDateWithIsoFormatting() { + WebConversionService conversionService = new WebConversionService(new DateTimeFormatters().dateFormat("iso")); + LocalDate date = conversionService.convert("2018-01-01", LocalDate.class); + assertThat(date).isEqualTo(java.time.LocalDate.of(2018, 1, 1)); + } + + @Test + void convertFromStringToDateWithIsoFormatting() { + WebConversionService conversionService = new WebConversionService(new DateTimeFormatters().dateFormat("iso")); + Date date = conversionService.convert("2018-01-01", Date.class); + Calendar calendar = Calendar.getInstance(); + calendar.setTime(date); + assertThat(calendar.get(Calendar.YEAR)).isEqualTo(2018); + assertThat(calendar.get(Calendar.MONTH)).isEqualTo(Calendar.JANUARY); + assertThat(calendar.get(Calendar.DAY_OF_MONTH)).isEqualTo(1); + } + private void customDateFormat(Object input) { WebConversionService conversionService = new WebConversionService( new DateTimeFormatters().dateFormat("dd*MM*yyyy"));