diff --git a/spring-context/src/main/java/org/springframework/format/datetime/standard/DateTimeFormatterRegistrar.java b/spring-context/src/main/java/org/springframework/format/datetime/standard/DateTimeFormatterRegistrar.java index 1a2e72438f..7632965b23 100644 --- a/spring-context/src/main/java/org/springframework/format/datetime/standard/DateTimeFormatterRegistrar.java +++ b/spring-context/src/main/java/org/springframework/format/datetime/standard/DateTimeFormatterRegistrar.java @@ -29,7 +29,7 @@ import java.time.YearMonth; import java.time.ZonedDateTime; import java.time.format.DateTimeFormatter; import java.time.format.FormatStyle; -import java.util.HashMap; +import java.util.EnumMap; import java.util.Map; import org.springframework.format.FormatterRegistrar; @@ -56,18 +56,17 @@ public class DateTimeFormatterRegistrar implements FormatterRegistrar { /** - * User defined formatters. + * User-defined formatters. */ - private final Map formatters = new HashMap<>(); + private final Map formatters = new EnumMap<>(Type.class); /** * Factories used when specific formatters have not been specified. */ - private final Map factories; + private final Map factories = new EnumMap<>(Type.class); public DateTimeFormatterRegistrar() { - this.factories = new HashMap<>(); for (Type type : Type.values()) { this.factories.put(type, new DateTimeFormatterFactory()); } @@ -155,33 +154,38 @@ public class DateTimeFormatterRegistrar implements FormatterRegistrar { public void registerFormatters(FormatterRegistry registry) { DateTimeConverters.registerConverters(registry); - DateTimeFormatter dateFormatter = getFormatter(Type.DATE); - DateTimeFormatter timeFormatter = getFormatter(Type.TIME); - DateTimeFormatter dateTimeFormatter = getFormatter(Type.DATE_TIME); + DateTimeFormatter df = getFormatter(Type.DATE); + DateTimeFormatter tf = getFormatter(Type.TIME); + DateTimeFormatter dtf = getFormatter(Type.DATE_TIME); + + // Efficient ISO_LOCAL_* variants for printing since they are twice as fast... registry.addFormatterForFieldType(LocalDate.class, - new TemporalAccessorPrinter(dateFormatter), - new TemporalAccessorParser(LocalDate.class, dateFormatter)); + new TemporalAccessorPrinter( + df == DateTimeFormatter.ISO_DATE ? DateTimeFormatter.ISO_LOCAL_DATE : df), + new TemporalAccessorParser(LocalDate.class, df)); registry.addFormatterForFieldType(LocalTime.class, - new TemporalAccessorPrinter(timeFormatter), - new TemporalAccessorParser(LocalTime.class, timeFormatter)); + new TemporalAccessorPrinter( + tf == DateTimeFormatter.ISO_TIME ? DateTimeFormatter.ISO_LOCAL_TIME : tf), + new TemporalAccessorParser(LocalTime.class, tf)); registry.addFormatterForFieldType(LocalDateTime.class, - new TemporalAccessorPrinter(dateTimeFormatter), - new TemporalAccessorParser(LocalDateTime.class, dateTimeFormatter)); + new TemporalAccessorPrinter( + dtf == DateTimeFormatter.ISO_DATE_TIME ? DateTimeFormatter.ISO_LOCAL_DATE_TIME : dtf), + new TemporalAccessorParser(LocalDateTime.class, dtf)); registry.addFormatterForFieldType(ZonedDateTime.class, - new TemporalAccessorPrinter(dateTimeFormatter), - new TemporalAccessorParser(ZonedDateTime.class, dateTimeFormatter)); + new TemporalAccessorPrinter(dtf), + new TemporalAccessorParser(ZonedDateTime.class, dtf)); registry.addFormatterForFieldType(OffsetDateTime.class, - new TemporalAccessorPrinter(dateTimeFormatter), - new TemporalAccessorParser(OffsetDateTime.class, dateTimeFormatter)); + new TemporalAccessorPrinter(dtf), + new TemporalAccessorParser(OffsetDateTime.class, dtf)); registry.addFormatterForFieldType(OffsetTime.class, - new TemporalAccessorPrinter(timeFormatter), - new TemporalAccessorParser(OffsetTime.class, timeFormatter)); + new TemporalAccessorPrinter(tf), + new TemporalAccessorParser(OffsetTime.class, tf)); registry.addFormatterForFieldType(Instant.class, new InstantFormatter()); registry.addFormatterForFieldType(Period.class, new PeriodFormatter()); diff --git a/spring-context/src/main/java/org/springframework/format/datetime/standard/Jsr310DateTimeFormatAnnotationFormatterFactory.java b/spring-context/src/main/java/org/springframework/format/datetime/standard/Jsr310DateTimeFormatAnnotationFormatterFactory.java index 932c52887f..9717620eb8 100644 --- a/spring-context/src/main/java/org/springframework/format/datetime/standard/Jsr310DateTimeFormatAnnotationFormatterFactory.java +++ b/spring-context/src/main/java/org/springframework/format/datetime/standard/Jsr310DateTimeFormatAnnotationFormatterFactory.java @@ -68,6 +68,24 @@ public class Jsr310DateTimeFormatAnnotationFormatterFactory extends EmbeddedValu @Override public Printer getPrinter(DateTimeFormat annotation, Class fieldType) { DateTimeFormatter formatter = getFormatter(annotation, fieldType); + + // Efficient ISO_LOCAL_* variants for printing since they are twice as fast... + if (formatter == DateTimeFormatter.ISO_DATE) { + if (isLocal(fieldType)) { + formatter = DateTimeFormatter.ISO_LOCAL_DATE; + } + } + else if (formatter == DateTimeFormatter.ISO_TIME) { + if (isLocal(fieldType)) { + formatter = DateTimeFormatter.ISO_LOCAL_TIME; + } + } + else if (formatter == DateTimeFormatter.ISO_DATE_TIME) { + if (isLocal(fieldType)) { + formatter = DateTimeFormatter.ISO_LOCAL_DATE_TIME; + } + } + return new TemporalAccessorPrinter(formatter); } @@ -81,7 +99,7 @@ public class Jsr310DateTimeFormatAnnotationFormatterFactory extends EmbeddedValu /** * Factory method used to create a {@link DateTimeFormatter}. * @param annotation the format annotation for the field - * @param fieldType the type of field + * @param fieldType the declared type of the field * @return a {@link DateTimeFormatter} instance */ protected DateTimeFormatter getFormatter(DateTimeFormat annotation, Class fieldType) { @@ -92,4 +110,8 @@ public class Jsr310DateTimeFormatAnnotationFormatterFactory extends EmbeddedValu return factory.createDateTimeFormatter(); } + private boolean isLocal(Class fieldType) { + return fieldType.getSimpleName().startsWith("Local"); + } + } diff --git a/spring-context/src/test/java/org/springframework/format/datetime/standard/DateTimeFormattingTests.java b/spring-context/src/test/java/org/springframework/format/datetime/standard/DateTimeFormattingTests.java index 6b78ce0d71..2eda9eddd8 100644 --- a/spring-context/src/test/java/org/springframework/format/datetime/standard/DateTimeFormattingTests.java +++ b/spring-context/src/test/java/org/springframework/format/datetime/standard/DateTimeFormattingTests.java @@ -311,6 +311,15 @@ public class DateTimeFormattingTests { @Test public void testBindISOTime() { + MutablePropertyValues propertyValues = new MutablePropertyValues(); + propertyValues.add("isoTime", "12:00:00"); + binder.bind(propertyValues); + assertEquals(0, binder.getBindingResult().getErrorCount()); + assertEquals("12:00:00", binder.getBindingResult().getFieldValue("isoTime")); + } + + @Test + public void testBindISOTimeWithZone() { MutablePropertyValues propertyValues = new MutablePropertyValues(); propertyValues.add("isoTime", "12:00:00.000-05:00"); binder.bind(propertyValues); @@ -320,6 +329,15 @@ public class DateTimeFormattingTests { @Test public void testBindISODateTime() { + MutablePropertyValues propertyValues = new MutablePropertyValues(); + propertyValues.add("isoDateTime", "2009-10-31T12:00:00"); + binder.bind(propertyValues); + assertEquals(0, binder.getBindingResult().getErrorCount()); + assertEquals("2009-10-31T12:00:00", binder.getBindingResult().getFieldValue("isoDateTime")); + } + + @Test + public void testBindISODateTimeWithZone() { MutablePropertyValues propertyValues = new MutablePropertyValues(); propertyValues.add("isoDateTime", "2009-10-31T12:00:00.000Z"); binder.bind(propertyValues); @@ -386,29 +404,29 @@ public class DateTimeFormattingTests { private LocalDate localDate; - @DateTimeFormat(style="M-") + @DateTimeFormat(style = "M-") private LocalDate localDateAnnotated; private LocalTime localTime; - @DateTimeFormat(style="-M") + @DateTimeFormat(style = "-M") private LocalTime localTimeAnnotated; private LocalDateTime localDateTime; - @DateTimeFormat(style="MM") + @DateTimeFormat(style = "MM") private LocalDateTime localDateTimeAnnotated; - @DateTimeFormat(pattern="M/d/yy h:mm a") + @DateTimeFormat(pattern = "M/d/yy h:mm a") private LocalDateTime dateTimeAnnotatedPattern; - @DateTimeFormat(iso=ISO.DATE) + @DateTimeFormat(iso = ISO.DATE) private LocalDate isoDate; - @DateTimeFormat(iso=ISO.TIME) + @DateTimeFormat(iso = ISO.TIME) private LocalTime isoTime; - @DateTimeFormat(iso=ISO.DATE_TIME) + @DateTimeFormat(iso = ISO.DATE_TIME) private LocalDateTime isoDateTime; private Instant instant;