diff --git a/org.springframework.context/src/main/java/org/springframework/format/datetime/joda/JodaDateTimeFormatAnnotationFormatterFactory.java b/org.springframework.context/src/main/java/org/springframework/format/datetime/joda/JodaDateTimeFormatAnnotationFormatterFactory.java index 227729ad52b..ad8bbb5beb1 100644 --- a/org.springframework.context/src/main/java/org/springframework/format/datetime/joda/JodaDateTimeFormatAnnotationFormatterFactory.java +++ b/org.springframework.context/src/main/java/org/springframework/format/datetime/joda/JodaDateTimeFormatAnnotationFormatterFactory.java @@ -22,7 +22,6 @@ import java.util.Date; import java.util.HashSet; import java.util.Set; -import org.joda.time.DateMidnight; import org.joda.time.DateTime; import org.joda.time.LocalDate; import org.joda.time.LocalDateTime; @@ -30,7 +29,6 @@ import org.joda.time.LocalTime; import org.joda.time.ReadableInstant; import org.joda.time.ReadablePartial; import org.joda.time.format.DateTimeFormatter; - import org.springframework.context.EmbeddedValueResolverAware; import org.springframework.format.AnnotationFormatterFactory; import org.springframework.format.Parser; @@ -57,16 +55,7 @@ public class JodaDateTimeFormatAnnotationFormatterFactory public JodaDateTimeFormatAnnotationFormatterFactory() { - Set> rawFieldTypes = new HashSet>(8); - rawFieldTypes.add(LocalDate.class); - rawFieldTypes.add(LocalTime.class); - rawFieldTypes.add(LocalDateTime.class); - rawFieldTypes.add(DateTime.class); - rawFieldTypes.add(DateMidnight.class); - rawFieldTypes.add(Date.class); - rawFieldTypes.add(Calendar.class); - rawFieldTypes.add(Long.class); - this.fieldTypes = Collections.unmodifiableSet(rawFieldTypes); + this.fieldTypes = createFieldTypes(); } public final Set> getFieldTypes() { @@ -105,7 +94,26 @@ public class JodaDateTimeFormatAnnotationFormatterFactory return new DateTimeParser(configureDateTimeFormatterFrom(annotation)); } + // internal helpers + /** + * Create the set of field types that may be annotated with @DateTimeFormat. + * Note: the 3 ReadablePartial concrete types are registered explicitly since addFormatterForFieldType rules exist for each of these types + * (if we did not do this, the default byType rules for LocalDate, LocalTime, and LocalDateTime would take precedence over the annotation rule, which is not what we want) + * @see JodaTimeFormatterRegistrar#registerFormatters(org.springframework.format.FormatterRegistry) + */ + private Set> createFieldTypes() { + Set> rawFieldTypes = new HashSet>(7); + rawFieldTypes.add(ReadableInstant.class); + rawFieldTypes.add(LocalDate.class); + rawFieldTypes.add(LocalTime.class); + rawFieldTypes.add(LocalDateTime.class); + rawFieldTypes.add(Date.class); + rawFieldTypes.add(Calendar.class); + rawFieldTypes.add(Long.class); + return Collections.unmodifiableSet(rawFieldTypes); + } + private DateTimeFormatter configureDateTimeFormatterFrom(DateTimeFormat annotation) { if (StringUtils.hasLength(annotation.pattern())) { return forPattern(resolveEmbeddedValue(annotation.pattern())); diff --git a/org.springframework.context/src/main/java/org/springframework/format/datetime/joda/JodaTimeConverters.java b/org.springframework.context/src/main/java/org/springframework/format/datetime/joda/JodaTimeConverters.java index b8840c7cdad..05fc3c03dbc 100644 --- a/org.springframework.context/src/main/java/org/springframework/format/datetime/joda/JodaTimeConverters.java +++ b/org.springframework.context/src/main/java/org/springframework/format/datetime/joda/JodaTimeConverters.java @@ -21,11 +21,12 @@ import java.util.Date; import org.joda.time.DateMidnight; import org.joda.time.DateTime; +import org.joda.time.Instant; import org.joda.time.LocalDate; import org.joda.time.LocalDateTime; import org.joda.time.LocalTime; +import org.joda.time.MutableDateTime; import org.joda.time.ReadableInstant; - import org.springframework.core.convert.converter.Converter; import org.springframework.core.convert.converter.ConverterRegistry; @@ -46,6 +47,8 @@ final class JodaTimeConverters { registry.addConverter(new DateTimeToLocalTimeConverter()); registry.addConverter(new DateTimeToLocalDateTimeConverter()); registry.addConverter(new DateTimeToDateMidnightConverter()); + registry.addConverter(new DateTimeToInstantConverter()); + registry.addConverter(new DateTimeToMutableDateTimeConverter()); registry.addConverter(new DateTimeToDateConverter()); registry.addConverter(new DateTimeToCalendarConverter()); registry.addConverter(new DateTimeToLongConverter()); @@ -94,6 +97,26 @@ final class JodaTimeConverters { } } + /** + * Used when binding a parsed DateTime to an Instant field. + * @see DateTimeParser + */ + private static class DateTimeToInstantConverter implements Converter { + public Instant convert(DateTime source) { + return source.toInstant(); + } + } + + /** + * Used when binding a parsed DateTime to a MutableDateTime field. + * @see DateTimeParser + */ + private static class DateTimeToMutableDateTimeConverter implements Converter { + public MutableDateTime convert(DateTime source) { + return source.toMutableDateTime(); + } + } + /** * Used when binding a parsed DateTime to a java.util.Date field. * @see DateTimeParser diff --git a/org.springframework.context/src/main/java/org/springframework/format/datetime/joda/JodaTimeFormatterRegistrar.java b/org.springframework.context/src/main/java/org/springframework/format/datetime/joda/JodaTimeFormatterRegistrar.java index af19ed8102d..3fab08ef8d5 100644 --- a/org.springframework.context/src/main/java/org/springframework/format/datetime/joda/JodaTimeFormatterRegistrar.java +++ b/org.springframework.context/src/main/java/org/springframework/format/datetime/joda/JodaTimeFormatterRegistrar.java @@ -117,7 +117,6 @@ public class JodaTimeFormatterRegistrar implements FormatterRegistrar { } if (this.dateStyle != null) { return DateTimeFormat.forStyle(this.dateStyle + "-"); - } else { return DateTimeFormat.shortDate(); } diff --git a/org.springframework.context/src/test/java/org/springframework/format/datetime/joda/JodaTimeFormattingTests.java b/org.springframework.context/src/test/java/org/springframework/format/datetime/joda/JodaTimeFormattingTests.java index b2b8338d0af..1539f69d04e 100644 --- a/org.springframework.context/src/test/java/org/springframework/format/datetime/joda/JodaTimeFormattingTests.java +++ b/org.springframework.context/src/test/java/org/springframework/format/datetime/joda/JodaTimeFormattingTests.java @@ -26,9 +26,11 @@ import java.util.Locale; import org.joda.time.DateTime; import org.joda.time.DateTimeZone; +import org.joda.time.Instant; import org.joda.time.LocalDate; import org.joda.time.LocalDateTime; import org.joda.time.LocalTime; +import org.joda.time.MutableDateTime; import org.junit.After; import org.junit.Before; import org.junit.Test; @@ -310,6 +312,41 @@ public class JodaTimeFormattingTests { assertEquals("2009-10-31T07:00:00.000-05:00", binder.getBindingResult().getFieldValue("isoDateTime")); } + @Test + public void testBindInstant() { + MutablePropertyValues propertyValues = new MutablePropertyValues(); + propertyValues.add("instant", "10/31/09 12:00 PM"); + binder.bind(propertyValues); + assertEquals(0, binder.getBindingResult().getErrorCount()); + assertEquals("10/31/09 12:00 PM", binder.getBindingResult().getFieldValue("instant")); + } + + @Test + public void testBindInstantAnnotated() { + MutablePropertyValues propertyValues = new MutablePropertyValues(); + propertyValues.add("instantAnnotated", "2009-10-31T12:00:00.000Z"); + binder.bind(propertyValues); + assertEquals(0, binder.getBindingResult().getErrorCount()); + assertEquals("2009-10-31T07:00:00.000-05:00", binder.getBindingResult().getFieldValue("instantAnnotated")); + } + + @Test + public void testBindMutableDateTime() { + MutablePropertyValues propertyValues = new MutablePropertyValues(); + propertyValues.add("mutableDateTime", "10/31/09 12:00 PM"); + binder.bind(propertyValues); + assertEquals(0, binder.getBindingResult().getErrorCount()); + assertEquals("10/31/09 12:00 PM", binder.getBindingResult().getFieldValue("mutableDateTime")); + } + + @Test + public void testBindMutableDateTimeAnnotated() { + MutablePropertyValues propertyValues = new MutablePropertyValues(); + propertyValues.add("mutableDateTimeAnnotated", "2009-10-31T12:00:00.000Z"); + binder.bind(propertyValues); + assertEquals(0, binder.getBindingResult().getErrorCount()); + assertEquals("2009-10-31T07:00:00.000-05:00", binder.getBindingResult().getFieldValue("mutableDateTimeAnnotated")); + } @SuppressWarnings("unused") private static class JodaTimeBean { @@ -368,6 +405,16 @@ public class JodaTimeFormattingTests { @DateTimeFormat(iso=ISO.DATE_TIME) private DateTime isoDateTime; + private Instant instant; + + @DateTimeFormat(iso=ISO.DATE_TIME) + private Instant instantAnnotated; + + private MutableDateTime mutableDateTime; + + @DateTimeFormat(iso=ISO.DATE_TIME) + private Instant mutableDateTimeAnnotated; + private final List children = new ArrayList(); public LocalDate getLocalDate() { @@ -530,6 +577,38 @@ public class JodaTimeFormattingTests { public void setIsoDateTime(DateTime isoDateTime) { this.isoDateTime = isoDateTime; } + + public Instant getInstant() { + return instant; + } + + public void setInstant(Instant instant) { + this.instant = instant; + } + + public Instant getInstantAnnotated() { + return instantAnnotated; + } + + public void setInstantAnnotated(Instant instantAnnotated) { + this.instantAnnotated = instantAnnotated; + } + + public MutableDateTime getMutableDateTime() { + return mutableDateTime; + } + + public void setMutableDateTime(MutableDateTime mutableDateTime) { + this.mutableDateTime = mutableDateTime; + } + + public Instant getMutableDateTimeAnnotated() { + return mutableDateTimeAnnotated; + } + + public void setMutableDateTimeAnnotated(Instant mutableDateTimeAnnotated) { + this.mutableDateTimeAnnotated = mutableDateTimeAnnotated; + } public List getChildren() { return children;