diff --git a/spring-context/src/main/java/org/springframework/format/datetime/joda/JodaTimeConverters.java b/spring-context/src/main/java/org/springframework/format/datetime/joda/JodaTimeConverters.java index 02ba64b613..b5a3b09dc4 100644 --- a/spring-context/src/main/java/org/springframework/format/datetime/joda/JodaTimeConverters.java +++ b/spring-context/src/main/java/org/springframework/format/datetime/joda/JodaTimeConverters.java @@ -36,8 +36,13 @@ import org.springframework.format.datetime.DateFormatterRegistrar; * Installs lower-level type converters required to integrate * Joda-Time support into Spring's field formatting system. * + *
Note: {@link JodaTimeFormatterRegistrar} installs these converters
+ * and relies on several of them for its formatters. Some additional
+ * converters are just being registered for custom conversion scenarios.
+ *
* @author Keith Donald
* @author Phillip Webb
+ * @author Juergen Hoeller
* @since 3.0
*/
final class JodaTimeConverters {
@@ -48,6 +53,7 @@ final class JodaTimeConverters {
*/
public static void registerConverters(ConverterRegistry registry) {
DateFormatterRegistrar.addDateConverters(registry);
+
registry.addConverter(new DateTimeToLocalDateConverter());
registry.addConverter(new DateTimeToLocalTimeConverter());
registry.addConverter(new DateTimeToLocalDateTimeConverter());
@@ -59,6 +65,11 @@ final class JodaTimeConverters {
registry.addConverter(new DateTimeToLongConverter());
registry.addConverter(new DateToReadableInstantConverter());
registry.addConverter(new CalendarToReadableInstantConverter());
+ registry.addConverter(new LongToReadableInstantConverter());
+ registry.addConverter(new LocalDateTimeToLocalDateConverter());
+ registry.addConverter(new LocalDateTimeToLocalTimeConverter());
+ registry.addConverter(new LocalDateToDateMidnightConverter());
+ registry.addConverter(new DateMidnightToLocalDateConverter());
}
@@ -144,7 +155,7 @@ final class JodaTimeConverters {
/**
- * Used when printing a java.util.Date field with a ReadableInstantPrinter.
+ * Used when printing a {@code java.util.Date} field with a ReadableInstantPrinter.
* @see MillisecondInstantPrinter
* @see JodaDateTimeFormatAnnotationFormatterFactory
*/
@@ -158,7 +169,7 @@ final class JodaTimeConverters {
/**
- * Used when printing a java.util.Calendar field with a ReadableInstantPrinter.
+ * Used when printing a {@code java.util.Calendar} field with a ReadableInstantPrinter.
* @see MillisecondInstantPrinter
* @see JodaDateTimeFormatAnnotationFormatterFactory
*/
@@ -170,4 +181,54 @@ final class JodaTimeConverters {
}
}
+
+ /**
+ * Used when printing a Long field with a ReadableInstantPrinter.
+ * @see MillisecondInstantPrinter
+ * @see JodaDateTimeFormatAnnotationFormatterFactory
+ */
+ private static class LongToReadableInstantConverter implements Converter Note: {@link DateTimeFormatterRegistrar} installs these converters but
+ * does not rely on them for its formatters. They are just being registered
+ * for custom conversion scenarios between different JSR-310 value types
+ * and also between {@link java.util.Calendar} and JSR-310 value types.
+ *
+ * @author Juergen Hoeller
+ * @since 4.0.1
+ */
+final class DateTimeConverters {
+
+ /**
+ * Install the converters into the converter registry.
+ * @param registry the converter registry
+ */
+ public static void registerConverters(ConverterRegistry registry) {
+ DateFormatterRegistrar.addDateConverters(registry);
+
+ registry.addConverter(new LocalDateTimeToLocalDateConverter());
+ registry.addConverter(new LocalDateTimeToLocalTimeConverter());
+ registry.addConverter(new ZonedDateTimeToLocalDateConverter());
+ registry.addConverter(new ZonedDateTimeToLocalTimeConverter());
+ registry.addConverter(new ZonedDateTimeToLocalDateTimeConverter());
+ registry.addConverter(new ZonedDateTimeToOffsetDateTimeConverter());
+ registry.addConverter(new ZonedDateTimeToInstantConverter());
+ registry.addConverter(new OffsetDateTimeToLocalDateConverter());
+ registry.addConverter(new OffsetDateTimeToLocalTimeConverter());
+ registry.addConverter(new OffsetDateTimeToLocalDateTimeConverter());
+ registry.addConverter(new OffsetDateTimeToZonedDateTimeConverter());
+ registry.addConverter(new OffsetDateTimeToInstantConverter());
+ registry.addConverter(new CalendarToZonedDateTimeConverter());
+ registry.addConverter(new CalendarToOffsetDateTimeConverter());
+ registry.addConverter(new CalendarToLocalDateConverter());
+ registry.addConverter(new CalendarToLocalTimeConverter());
+ registry.addConverter(new CalendarToLocalDateTimeConverter());
+ registry.addConverter(new CalendarToInstantConverter());
+ registry.addConverter(new LongToInstantConverter());
+ registry.addConverter(new InstantToLongConverter());
+ }
+
+ private static ZonedDateTime calendarToZonedDateTime(Calendar source) {
+ if (source instanceof GregorianCalendar) {
+ return ((GregorianCalendar) source).toZonedDateTime();
+ }
+ else {
+ return ZonedDateTime.ofInstant(Instant.ofEpochMilli(source.getTimeInMillis()),
+ source.getTimeZone().toZoneId());
+ }
+ }
+
+
+ private static class LocalDateTimeToLocalDateConverter implements Converter This formatter will be used for the {@link org.joda.time.LocalDate} type.
+ * This formatter will be used for the {@link LocalDate} type.
* When specified, the {@link #setDateStyle dateStyle} and
* {@link #setUseIsoFormat useIsoFormat} properties will be ignored.
* @param formatter the formatter to use
@@ -121,8 +121,8 @@ public class DateTimeFormatterRegistrar implements FormatterRegistrar {
/**
* Set the formatter that will be used for objects representing time values.
- * This formatter will be used for the {@link org.joda.time.LocalTime} type.
- * When specified, the {@link #setTimeStyle timeStyle} and
+ * This formatter will be used for the {@link LocalTime} and {@link OffsetTime}
+ * types. When specified, the {@link #setTimeStyle timeStyle} and
* {@link #setUseIsoFormat useIsoFormat} properties will be ignored.
* @param formatter the formatter to use
* @see #setDateFormatter
@@ -134,9 +134,9 @@ public class DateTimeFormatterRegistrar implements FormatterRegistrar {
/**
* Set the formatter that will be used for objects representing date and time values.
- * This formatter will be used for {@link org.joda.time.LocalDateTime}, {@link org.joda.time.ReadableInstant},
- * {@link java.util.Date} and {@link java.util.Calendar} types.
- * When specified, the {@link #setDateTimeStyle dateTimeStyle} and
+ * This formatter will be used for {@link LocalDateTime}, {@link ZonedDateTime}
+ * and {@link OffsetDateTime} types. When specified, the
+ * {@link #setDateTimeStyle dateTimeStyle} and
* {@link #setUseIsoFormat useIsoFormat} properties will be ignored.
* @param formatter the formatter to use
* @see #setDateFormatter
@@ -149,6 +149,8 @@ public class DateTimeFormatterRegistrar implements FormatterRegistrar {
@Override
public void registerFormatters(FormatterRegistry registry) {
+ DateTimeConverters.registerConverters(registry);
+
DateTimeFormatter dateFormatter = getFormatter(Type.DATE);
DateTimeFormatter timeFormatter = getFormatter(Type.TIME);
DateTimeFormatter dateTimeFormatter = getFormatter(Type.DATE_TIME);
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 d12f311061..ceefd049f9 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
@@ -16,7 +16,7 @@
package org.springframework.format.datetime.standard;
-import java.lang.reflect.Method;
+import java.text.DateFormat;
import java.time.Instant;
import java.time.LocalDate;
import java.time.LocalDateTime;
@@ -25,8 +25,10 @@ import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.time.format.FormatStyle;
import java.util.ArrayList;
+import java.util.GregorianCalendar;
import java.util.List;
import java.util.Locale;
+import java.util.TimeZone;
import org.junit.After;
import org.junit.Before;
@@ -34,7 +36,6 @@ import org.junit.Test;
import org.springframework.beans.MutablePropertyValues;
import org.springframework.context.i18n.LocaleContextHolder;
-import org.springframework.core.LocalVariableTableParameterNameDiscoverer;
import org.springframework.core.convert.support.DefaultConversionService;
import org.springframework.format.annotation.DateTimeFormat;
import org.springframework.format.annotation.DateTimeFormat.ISO;
@@ -171,6 +172,15 @@ public class DateTimeFormattingTests {
assertEquals("Oct -31, 2009", binder.getBindingResult().getFieldValue("localDateAnnotated"));
}
+ @Test
+ public void testBindLocalDateFromJavaUtilCalendar() throws Exception {
+ MutablePropertyValues propertyValues = new MutablePropertyValues();
+ propertyValues.add("localDate", new GregorianCalendar(2009, 9, 31, 0, 0));
+ binder.bind(propertyValues);
+ assertEquals(0, binder.getBindingResult().getErrorCount());
+ assertEquals("10/31/09", binder.getBindingResult().getFieldValue("localDate"));
+ }
+
@Test
public void testBindLocalTime() {
MutablePropertyValues propertyValues = new MutablePropertyValues();
@@ -213,6 +223,15 @@ public class DateTimeFormattingTests {
assertEquals("12:00:00 PM", binder.getBindingResult().getFieldValue("localTimeAnnotated"));
}
+ @Test
+ public void testBindLocalTimeFromJavaUtilCalendar() throws Exception {
+ MutablePropertyValues propertyValues = new MutablePropertyValues();
+ propertyValues.add("localTime", new GregorianCalendar(1970, 0, 0, 12, 0));
+ binder.bind(propertyValues);
+ assertEquals(0, binder.getBindingResult().getErrorCount());
+ assertEquals("12:00 PM", binder.getBindingResult().getFieldValue("localTime"));
+ }
+
@Test
public void testBindLocalDateTime() {
MutablePropertyValues propertyValues = new MutablePropertyValues();
@@ -231,6 +250,15 @@ public class DateTimeFormattingTests {
assertEquals("Oct 31, 2009 12:00:00 PM", binder.getBindingResult().getFieldValue("localDateTimeAnnotated"));
}
+ @Test
+ public void testBindLocalDateTimeFromJavaUtilCalendar() throws Exception {
+ MutablePropertyValues propertyValues = new MutablePropertyValues();
+ propertyValues.add("localDateTime", new GregorianCalendar(2009, 9, 31, 12, 0));
+ binder.bind(propertyValues);
+ assertEquals(0, binder.getBindingResult().getErrorCount());
+ assertEquals("10/31/09 12:00 PM", binder.getBindingResult().getFieldValue("localDateTime"));
+ }
+
@Test
public void testBindDateTimeWithSpecificStyle() throws Exception {
DateTimeFormatterRegistrar registrar = new DateTimeFormatterRegistrar();
@@ -241,9 +269,6 @@ public class DateTimeFormattingTests {
binder.bind(propertyValues);
assertEquals(0, binder.getBindingResult().getErrorCount());
assertEquals("Oct 31, 2009 12:00:00 PM", binder.getBindingResult().getFieldValue("localDateTime"));
- Method testMethod = LocalVariableTableParameterNameDiscoverer.class.getMethod("getParameterNames", Method.class);
- System.out.println(testMethod.getParameters()[0].getName());
- System.out.println(new LocalVariableTableParameterNameDiscoverer().getParameterNames(testMethod)[0]);
}
@Test
@@ -291,6 +316,17 @@ public class DateTimeFormattingTests {
assertTrue(binder.getBindingResult().getFieldValue("instant").toString().startsWith("2009-10-31T12:00"));
}
+ @Test
+ public void testBindInstantFromJavaUtilDate() throws Exception {
+ DateFormat df = DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.SHORT, Locale.US);
+ df.setTimeZone(TimeZone.getTimeZone("GMT"));
+ MutablePropertyValues propertyValues = new MutablePropertyValues();
+ propertyValues.add("instant", df.parse("10/31/09 12:00 PM"));
+ binder.bind(propertyValues);
+ assertEquals(0, binder.getBindingResult().getErrorCount());
+ assertTrue(binder.getBindingResult().getFieldValue("instant").toString().startsWith("2009-10-31T12:00"));
+ }
+
public static class DateTimeBean {