Added extensive default converters for JSR-310 value types
Also adding several further Joda-Time converters for consistency with JSR-310 converters. Issue: SPR-11259
This commit is contained in:
@@ -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.
|
||||
*
|
||||
* <p>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<Long, ReadableInstant> {
|
||||
|
||||
@Override
|
||||
public ReadableInstant convert(Long source) {
|
||||
return new DateTime(source.longValue());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static class LocalDateTimeToLocalDateConverter implements Converter<LocalDateTime, LocalDate> {
|
||||
|
||||
@Override
|
||||
public LocalDate convert(LocalDateTime source) {
|
||||
return source.toLocalDate();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static class LocalDateTimeToLocalTimeConverter implements Converter<LocalDateTime, LocalTime> {
|
||||
|
||||
@Override
|
||||
public LocalTime convert(LocalDateTime source) {
|
||||
return source.toLocalTime();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static class LocalDateToDateMidnightConverter implements Converter<LocalDate, DateMidnight> {
|
||||
|
||||
@Override
|
||||
public DateMidnight convert(LocalDate source) {
|
||||
return source.toDateMidnight();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static class DateMidnightToLocalDateConverter implements Converter<DateMidnight, LocalDate> {
|
||||
|
||||
@Override
|
||||
public LocalDate convert(DateMidnight source) {
|
||||
return source.toLocalDate();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,267 @@
|
||||
/*
|
||||
* Copyright 2002-2013 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.format.datetime.standard;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.LocalTime;
|
||||
import java.time.OffsetDateTime;
|
||||
import java.time.ZonedDateTime;
|
||||
import java.time.chrono.ChronoZonedDateTime;
|
||||
import java.util.Calendar;
|
||||
import java.util.GregorianCalendar;
|
||||
|
||||
import org.springframework.core.convert.converter.Converter;
|
||||
import org.springframework.core.convert.converter.ConverterRegistry;
|
||||
import org.springframework.format.datetime.DateFormatterRegistrar;
|
||||
|
||||
/**
|
||||
* Installs lower-level type converters required to integrate
|
||||
* JSR-310 support into Spring's field formatting system.
|
||||
*
|
||||
* <p>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<LocalDateTime, LocalDate> {
|
||||
|
||||
@Override
|
||||
public LocalDate convert(LocalDateTime source) {
|
||||
return source.toLocalDate();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static class LocalDateTimeToLocalTimeConverter implements Converter<LocalDateTime, LocalTime> {
|
||||
|
||||
@Override
|
||||
public LocalTime convert(LocalDateTime source) {
|
||||
return source.toLocalTime();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static class ZonedDateTimeToLocalDateConverter implements Converter<ZonedDateTime, LocalDate> {
|
||||
|
||||
@Override
|
||||
public LocalDate convert(ZonedDateTime source) {
|
||||
return source.toLocalDate();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static class ZonedDateTimeToLocalTimeConverter implements Converter<ZonedDateTime, LocalTime> {
|
||||
|
||||
@Override
|
||||
public LocalTime convert(ZonedDateTime source) {
|
||||
return source.toLocalTime();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static class ZonedDateTimeToLocalDateTimeConverter implements Converter<ZonedDateTime, LocalDateTime> {
|
||||
|
||||
@Override
|
||||
public LocalDateTime convert(ZonedDateTime source) {
|
||||
return source.toLocalDateTime();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static class ZonedDateTimeToOffsetDateTimeConverter implements Converter<ZonedDateTime, OffsetDateTime> {
|
||||
|
||||
@Override
|
||||
public OffsetDateTime convert(ZonedDateTime source) {
|
||||
return source.toOffsetDateTime();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static class ZonedDateTimeToInstantConverter implements Converter<ZonedDateTime, Instant> {
|
||||
|
||||
@Override
|
||||
public Instant convert(ZonedDateTime source) {
|
||||
// Explicit cast to interface necessary in order to call Java 8 default method from -source 1.6
|
||||
return ((ChronoZonedDateTime) source).toInstant();
|
||||
}
|
||||
}
|
||||
|
||||
private static class OffsetDateTimeToLocalDateConverter implements Converter<OffsetDateTime, LocalDate> {
|
||||
|
||||
@Override
|
||||
public LocalDate convert(OffsetDateTime source) {
|
||||
return source.toLocalDate();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static class OffsetDateTimeToLocalTimeConverter implements Converter<OffsetDateTime, LocalTime> {
|
||||
|
||||
@Override
|
||||
public LocalTime convert(OffsetDateTime source) {
|
||||
return source.toLocalTime();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static class OffsetDateTimeToLocalDateTimeConverter implements Converter<OffsetDateTime, LocalDateTime> {
|
||||
|
||||
@Override
|
||||
public LocalDateTime convert(OffsetDateTime source) {
|
||||
return source.toLocalDateTime();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static class OffsetDateTimeToZonedDateTimeConverter implements Converter<OffsetDateTime, ZonedDateTime> {
|
||||
|
||||
@Override
|
||||
public ZonedDateTime convert(OffsetDateTime source) {
|
||||
return source.toZonedDateTime();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static class OffsetDateTimeToInstantConverter implements Converter<OffsetDateTime, Instant> {
|
||||
|
||||
@Override
|
||||
public Instant convert(OffsetDateTime source) {
|
||||
return source.toInstant();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static class CalendarToZonedDateTimeConverter implements Converter<Calendar, ZonedDateTime> {
|
||||
|
||||
@Override
|
||||
public ZonedDateTime convert(Calendar source) {
|
||||
return calendarToZonedDateTime(source);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static class CalendarToOffsetDateTimeConverter implements Converter<Calendar, OffsetDateTime> {
|
||||
|
||||
@Override
|
||||
public OffsetDateTime convert(Calendar source) {
|
||||
return calendarToZonedDateTime(source).toOffsetDateTime();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static class CalendarToLocalDateConverter implements Converter<Calendar, LocalDate> {
|
||||
|
||||
@Override
|
||||
public LocalDate convert(Calendar source) {
|
||||
return calendarToZonedDateTime(source).toLocalDate();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static class CalendarToLocalTimeConverter implements Converter<Calendar, LocalTime> {
|
||||
|
||||
@Override
|
||||
public LocalTime convert(Calendar source) {
|
||||
return calendarToZonedDateTime(source).toLocalTime();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static class CalendarToLocalDateTimeConverter implements Converter<Calendar, LocalDateTime> {
|
||||
|
||||
@Override
|
||||
public LocalDateTime convert(Calendar source) {
|
||||
return calendarToZonedDateTime(source).toLocalDateTime();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static class CalendarToInstantConverter implements Converter<Calendar, Instant> {
|
||||
|
||||
@Override
|
||||
public Instant convert(Calendar source) {
|
||||
// Explicit cast to interface necessary in order to call Java 8 default method from -source 1.6
|
||||
return ((ChronoZonedDateTime) calendarToZonedDateTime(source)).toInstant();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static class LongToInstantConverter implements Converter<Long, Instant> {
|
||||
|
||||
@Override
|
||||
public Instant convert(Long source) {
|
||||
return Instant.ofEpochMilli(source);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static class InstantToLongConverter implements Converter<Instant, Long> {
|
||||
|
||||
@Override
|
||||
public Long convert(Instant source) {
|
||||
return source.toEpochMilli();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -108,7 +108,7 @@ public class DateTimeFormatterRegistrar implements FormatterRegistrar {
|
||||
|
||||
/**
|
||||
* Set the formatter that will be used for objects representing date values.
|
||||
* <p>This formatter will be used for the {@link org.joda.time.LocalDate} type.
|
||||
* <p>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.
|
||||
* <p>This formatter will be used for the {@link org.joda.time.LocalTime} type.
|
||||
* When specified, the {@link #setTimeStyle timeStyle} and
|
||||
* <p>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.
|
||||
* <p>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
|
||||
* <p>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);
|
||||
|
||||
@@ -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 {
|
||||
|
||||
|
||||
Reference in New Issue
Block a user