Skip to content

Commit 1ae17c2

Browse files
committed
Efficient ISO_LOCAL_* variants for printing LocalDate/LocalTime/LocalDateTime
Issue: SPR-14958
1 parent 5458a42 commit 1ae17c2

File tree

3 files changed

+72
-28
lines changed

3 files changed

+72
-28
lines changed

spring-context/src/main/java/org/springframework/format/datetime/standard/DateTimeFormatterRegistrar.java

Lines changed: 24 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@
2929
import java.time.ZonedDateTime;
3030
import java.time.format.DateTimeFormatter;
3131
import java.time.format.FormatStyle;
32-
import java.util.HashMap;
32+
import java.util.EnumMap;
3333
import java.util.Map;
3434

3535
import org.springframework.format.FormatterRegistrar;
@@ -56,18 +56,17 @@ private enum Type {DATE, TIME, DATE_TIME}
5656

5757

5858
/**
59-
* User defined formatters.
59+
* User-defined formatters.
6060
*/
61-
private final Map<Type, DateTimeFormatter> formatters = new HashMap<>();
61+
private final Map<Type, DateTimeFormatter> formatters = new EnumMap<>(Type.class);
6262

6363
/**
6464
* Factories used when specific formatters have not been specified.
6565
*/
66-
private final Map<Type, DateTimeFormatterFactory> factories;
66+
private final Map<Type, DateTimeFormatterFactory> factories = new EnumMap<>(Type.class);
6767

6868

6969
public DateTimeFormatterRegistrar() {
70-
this.factories = new HashMap<>();
7170
for (Type type : Type.values()) {
7271
this.factories.put(type, new DateTimeFormatterFactory());
7372
}
@@ -155,33 +154,38 @@ public void setDateTimeFormatter(DateTimeFormatter formatter) {
155154
public void registerFormatters(FormatterRegistry registry) {
156155
DateTimeConverters.registerConverters(registry);
157156

158-
DateTimeFormatter dateFormatter = getFormatter(Type.DATE);
159-
DateTimeFormatter timeFormatter = getFormatter(Type.TIME);
160-
DateTimeFormatter dateTimeFormatter = getFormatter(Type.DATE_TIME);
157+
DateTimeFormatter df = getFormatter(Type.DATE);
158+
DateTimeFormatter tf = getFormatter(Type.TIME);
159+
DateTimeFormatter dtf = getFormatter(Type.DATE_TIME);
160+
161+
// Efficient ISO_LOCAL_* variants for printing since they are twice as fast...
161162

162163
registry.addFormatterForFieldType(LocalDate.class,
163-
new TemporalAccessorPrinter(dateFormatter),
164-
new TemporalAccessorParser(LocalDate.class, dateFormatter));
164+
new TemporalAccessorPrinter(
165+
df == DateTimeFormatter.ISO_DATE ? DateTimeFormatter.ISO_LOCAL_DATE : df),
166+
new TemporalAccessorParser(LocalDate.class, df));
165167

166168
registry.addFormatterForFieldType(LocalTime.class,
167-
new TemporalAccessorPrinter(timeFormatter),
168-
new TemporalAccessorParser(LocalTime.class, timeFormatter));
169+
new TemporalAccessorPrinter(
170+
tf == DateTimeFormatter.ISO_TIME ? DateTimeFormatter.ISO_LOCAL_TIME : tf),
171+
new TemporalAccessorParser(LocalTime.class, tf));
169172

170173
registry.addFormatterForFieldType(LocalDateTime.class,
171-
new TemporalAccessorPrinter(dateTimeFormatter),
172-
new TemporalAccessorParser(LocalDateTime.class, dateTimeFormatter));
174+
new TemporalAccessorPrinter(
175+
dtf == DateTimeFormatter.ISO_DATE_TIME ? DateTimeFormatter.ISO_LOCAL_DATE_TIME : dtf),
176+
new TemporalAccessorParser(LocalDateTime.class, dtf));
173177

174178
registry.addFormatterForFieldType(ZonedDateTime.class,
175-
new TemporalAccessorPrinter(dateTimeFormatter),
176-
new TemporalAccessorParser(ZonedDateTime.class, dateTimeFormatter));
179+
new TemporalAccessorPrinter(dtf),
180+
new TemporalAccessorParser(ZonedDateTime.class, dtf));
177181

178182
registry.addFormatterForFieldType(OffsetDateTime.class,
179-
new TemporalAccessorPrinter(dateTimeFormatter),
180-
new TemporalAccessorParser(OffsetDateTime.class, dateTimeFormatter));
183+
new TemporalAccessorPrinter(dtf),
184+
new TemporalAccessorParser(OffsetDateTime.class, dtf));
181185

182186
registry.addFormatterForFieldType(OffsetTime.class,
183-
new TemporalAccessorPrinter(timeFormatter),
184-
new TemporalAccessorParser(OffsetTime.class, timeFormatter));
187+
new TemporalAccessorPrinter(tf),
188+
new TemporalAccessorParser(OffsetTime.class, tf));
185189

186190
registry.addFormatterForFieldType(Instant.class, new InstantFormatter());
187191
registry.addFormatterForFieldType(Period.class, new PeriodFormatter());

spring-context/src/main/java/org/springframework/format/datetime/standard/Jsr310DateTimeFormatAnnotationFormatterFactory.java

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,24 @@ public final Set<Class<?>> getFieldTypes() {
6868
@Override
6969
public Printer<?> getPrinter(DateTimeFormat annotation, Class<?> fieldType) {
7070
DateTimeFormatter formatter = getFormatter(annotation, fieldType);
71+
72+
// Efficient ISO_LOCAL_* variants for printing since they are twice as fast...
73+
if (formatter == DateTimeFormatter.ISO_DATE) {
74+
if (isLocal(fieldType)) {
75+
formatter = DateTimeFormatter.ISO_LOCAL_DATE;
76+
}
77+
}
78+
else if (formatter == DateTimeFormatter.ISO_TIME) {
79+
if (isLocal(fieldType)) {
80+
formatter = DateTimeFormatter.ISO_LOCAL_TIME;
81+
}
82+
}
83+
else if (formatter == DateTimeFormatter.ISO_DATE_TIME) {
84+
if (isLocal(fieldType)) {
85+
formatter = DateTimeFormatter.ISO_LOCAL_DATE_TIME;
86+
}
87+
}
88+
7189
return new TemporalAccessorPrinter(formatter);
7290
}
7391

@@ -81,7 +99,7 @@ public Parser<?> getParser(DateTimeFormat annotation, Class<?> fieldType) {
8199
/**
82100
* Factory method used to create a {@link DateTimeFormatter}.
83101
* @param annotation the format annotation for the field
84-
* @param fieldType the type of field
102+
* @param fieldType the declared type of the field
85103
* @return a {@link DateTimeFormatter} instance
86104
*/
87105
protected DateTimeFormatter getFormatter(DateTimeFormat annotation, Class<?> fieldType) {
@@ -92,4 +110,8 @@ protected DateTimeFormatter getFormatter(DateTimeFormat annotation, Class<?> fie
92110
return factory.createDateTimeFormatter();
93111
}
94112

113+
private boolean isLocal(Class<?> fieldType) {
114+
return fieldType.getSimpleName().startsWith("Local");
115+
}
116+
95117
}

spring-context/src/test/java/org/springframework/format/datetime/standard/DateTimeFormattingTests.java

Lines changed: 25 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -311,6 +311,15 @@ public void testBindISODate() {
311311

312312
@Test
313313
public void testBindISOTime() {
314+
MutablePropertyValues propertyValues = new MutablePropertyValues();
315+
propertyValues.add("isoTime", "12:00:00");
316+
binder.bind(propertyValues);
317+
assertEquals(0, binder.getBindingResult().getErrorCount());
318+
assertEquals("12:00:00", binder.getBindingResult().getFieldValue("isoTime"));
319+
}
320+
321+
@Test
322+
public void testBindISOTimeWithZone() {
314323
MutablePropertyValues propertyValues = new MutablePropertyValues();
315324
propertyValues.add("isoTime", "12:00:00.000-05:00");
316325
binder.bind(propertyValues);
@@ -320,6 +329,15 @@ public void testBindISOTime() {
320329

321330
@Test
322331
public void testBindISODateTime() {
332+
MutablePropertyValues propertyValues = new MutablePropertyValues();
333+
propertyValues.add("isoDateTime", "2009-10-31T12:00:00");
334+
binder.bind(propertyValues);
335+
assertEquals(0, binder.getBindingResult().getErrorCount());
336+
assertEquals("2009-10-31T12:00:00", binder.getBindingResult().getFieldValue("isoDateTime"));
337+
}
338+
339+
@Test
340+
public void testBindISODateTimeWithZone() {
323341
MutablePropertyValues propertyValues = new MutablePropertyValues();
324342
propertyValues.add("isoDateTime", "2009-10-31T12:00:00.000Z");
325343
binder.bind(propertyValues);
@@ -386,29 +404,29 @@ public static class DateTimeBean {
386404

387405
private LocalDate localDate;
388406

389-
@DateTimeFormat(style="M-")
407+
@DateTimeFormat(style = "M-")
390408
private LocalDate localDateAnnotated;
391409

392410
private LocalTime localTime;
393411

394-
@DateTimeFormat(style="-M")
412+
@DateTimeFormat(style = "-M")
395413
private LocalTime localTimeAnnotated;
396414

397415
private LocalDateTime localDateTime;
398416

399-
@DateTimeFormat(style="MM")
417+
@DateTimeFormat(style = "MM")
400418
private LocalDateTime localDateTimeAnnotated;
401419

402-
@DateTimeFormat(pattern="M/d/yy h:mm a")
420+
@DateTimeFormat(pattern = "M/d/yy h:mm a")
403421
private LocalDateTime dateTimeAnnotatedPattern;
404422

405-
@DateTimeFormat(iso=ISO.DATE)
423+
@DateTimeFormat(iso = ISO.DATE)
406424
private LocalDate isoDate;
407425

408-
@DateTimeFormat(iso=ISO.TIME)
426+
@DateTimeFormat(iso = ISO.TIME)
409427
private LocalTime isoTime;
410428

411-
@DateTimeFormat(iso=ISO.DATE_TIME)
429+
@DateTimeFormat(iso = ISO.DATE_TIME)
412430
private LocalDateTime isoDateTime;
413431

414432
private Instant instant;

0 commit comments

Comments
 (0)