From af41dd1ed6f56763eb4838dcdd02a7d6c5204970 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Wed, 30 Nov 2016 23:41:00 +0100 Subject: [PATCH] Efficient ISO_LOCAL_* variants for printing LocalDate/LocalTime/LocalDateTime Issue: SPR-14958 (cherry picked from commit 1ae17c2) --- .../standard/DateTimeFormatterRegistrar.java | 48 +++++++++++-------- ...eTimeFormatAnnotationFormatterFactory.java | 26 +++++++++- .../standard/DateTimeFormattingTests.java | 34 +++++++++---- 3 files changed, 77 insertions(+), 31 deletions(-) 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 07a65cc9607..23d8040390f 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 @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -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; @@ -58,18 +58,19 @@ 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()); } @@ -157,33 +158,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 62729c4a45c..56a431c7795 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 @@ -1,5 +1,5 @@ /* - * Copyright 2002-2014 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -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 3bc0efd3d56..2eda9eddd89 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; @@ -421,7 +439,7 @@ public class DateTimeFormattingTests { private MonthDay monthDay; - private final List children = new ArrayList(); + private final List children = new ArrayList<>(); public LocalDate getLocalDate() { return localDate;