Revised Joda-Time support (as a side effect of JSR-310 support in Spring 4.0)

Issue: SPR-9641
(cherry picked from commit b5d44e1)
This commit is contained in:
Juergen Hoeller 2013-04-23 13:50:25 +02:00 committed by unknown
parent 265c0c1505
commit d0513f7521
15 changed files with 246 additions and 233 deletions

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2012 the original author or authors. * Copyright 2002-2013 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -23,22 +23,26 @@ import java.lang.annotation.Target;
/** /**
* Declares that a field should be formatted as a date time. * Declares that a field should be formatted as a date time.
* Supports formatting by style pattern, ISO date time pattern, or custom format pattern string. *
* Can be applied to {@code java.util.Date}, {@code java.util.Calendar}, {@code java.long.Long}, or Joda Time fields. * <p>Supports formatting by style pattern, ISO date time pattern, or custom format pattern string.
* <p> * Can be applied to {@code java.util.Date}, {@code java.util.Calendar}, {@code java.long.Long},
* For style-based formatting, set the {@link #style()} attribute to be the style pattern code. * Joda-Time value types; and as of Spring 4 and JDK 8, to JSR-310 <code>java.time</code> types too.
*
* <p>For style-based formatting, set the {@link #style()} attribute to be the style pattern code.
* The first character of the code is the date style, and the second character is the time style. * The first character of the code is the date style, and the second character is the time style.
* Specify a character of 'S' for short style, 'M' for medium, 'L' for long, and 'F' for full. * Specify a character of 'S' for short style, 'M' for medium, 'L' for long, and 'F' for full.
* A date or time may be omitted by specifying the style character '-'. * A date or time may be omitted by specifying the style character '-'.
* <p> *
* For ISO-based formatting, set the {@link #iso()} attribute to be the desired {@link ISO} format, such as {@link ISO#DATE}. * <p>For ISO-based formatting, set the {@link #iso()} attribute to be the desired {@link ISO} format,
<p> * such as {@link ISO#DATE}. For custom formatting, set the {@link #pattern()} attribute to be the
* For custom formatting, set the {@link #pattern()} attribute to be the DateTime pattern, such as {@code yyyy/MM/dd hh:mm:ss a}. * DateTime pattern, such as {@code yyyy/MM/dd hh:mm:ss a}.
* <p> *
* Each attribute is mutually exclusive, so only set one attribute per annotation instance (the one most convenient one for your formatting needs). * <p>Each attribute is mutually exclusive, so only set one attribute per annotation instance
* (the one most convenient one for your formatting needs).
* When the pattern attribute is specified, it takes precedence over both the style and ISO attribute. * When the pattern attribute is specified, it takes precedence over both the style and ISO attribute.
* When the iso attribute is specified, if takes precedence over the style attribute. * When the iso attribute is specified, if takes precedence over the style attribute.
* When no annotation attributes are specified, the default format applied is style-based with a style code of 'SS' (short date, short time). * When no annotation attributes are specified, the default format applied is style-based
* with a style code of 'SS' (short date, short time).
* *
* @author Keith Donald * @author Keith Donald
* @author Juergen Hoeller * @author Juergen Hoeller
@ -51,23 +55,24 @@ public @interface DateTimeFormat {
/** /**
* The style pattern to use to format the field. * The style pattern to use to format the field.
* Defaults to 'SS' for short date time. * <p>Defaults to 'SS' for short date time. Set this attribute when you wish to format
* Set this attribute when you wish to format your field in accordance with a common style other than the default style. * your field in accordance with a common style other than the default style.
*/ */
String style() default "SS"; String style() default "SS";
/** /**
* The ISO pattern to use to format the field. * The ISO pattern to use to format the field.
* The possible ISO patterns are defined in the {@link ISO} enum. * The possible ISO patterns are defined in the {@link ISO} enum.
* Defaults to ISO.NONE, indicating this attribute should be ignored. * <p>Defaults to {@link ISO#NONE}, indicating this attribute should be ignored.
* Set this attribute when you wish to format your field in accordance with an ISO date time format. * Set this attribute when you wish to format your field in accordance with an ISO format.
*/ */
ISO iso() default ISO.NONE; ISO iso() default ISO.NONE;
/** /**
* The custom pattern to use to format the field. * The custom pattern to use to format the field.
* Defaults to empty String, indicating no custom pattern String has been specified. * <p>Defaults to empty String, indicating no custom pattern String has been specified.
* Set this attribute when you wish to format your field in accordance with a custom date time pattern not represented by a style or ISO format. * Set this attribute when you wish to format your field in accordance with a custom
* date time pattern not represented by a style or ISO format.
*/ */
String pattern() default ""; String pattern() default "";
@ -78,18 +83,21 @@ public @interface DateTimeFormat {
public enum ISO { public enum ISO {
/** /**
* The most common ISO Date Format {@code yyyy-MM-dd} e.g. 2000-10-31. * The most common ISO Date Format {@code yyyy-MM-dd},
* e.g. 2000-10-31.
*/ */
DATE, DATE,
/** /**
* The most common ISO Time Format {@code HH:mm:ss.SSSZ} e.g. 01:30:00.000-05:00. * The most common ISO Time Format {@code HH:mm:ss.SSSZ},
* e.g. 01:30:00.000-05:00.
*/ */
TIME, TIME,
/** /**
* The most common ISO DateTime Format {@code yyyy-MM-dd'T'HH:mm:ss.SSSZ} e.g. 2000-10-31 01:30:00.000-05:00. * The most common ISO DateTime Format {@code yyyy-MM-dd'T'HH:mm:ss.SSSZ},
* The default if no annotation value is specified. * e.g. 2000-10-31 01:30:00.000-05:00.
* <p>This is the default if no annotation value is specified.
*/ */
DATE_TIME, DATE_TIME,

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2012 the original author or authors. * Copyright 2002-2013 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -46,7 +46,7 @@ public class DateFormatter implements Formatter<Date> {
private static final Map<ISO, String> ISO_PATTERNS; private static final Map<ISO, String> ISO_PATTERNS;
static { static {
Map<ISO, String> formats = new HashMap<DateTimeFormat.ISO, String>(); Map<ISO, String> formats = new HashMap<DateTimeFormat.ISO, String>(4);
formats.put(ISO.DATE, "yyyy-MM-dd"); formats.put(ISO.DATE, "yyyy-MM-dd");
formats.put(ISO.TIME, "HH:mm:ss.SSSZ"); formats.put(ISO.TIME, "HH:mm:ss.SSSZ");
formats.put(ISO.DATE_TIME, "yyyy-MM-dd'T'HH:mm:ss.SSSZ"); formats.put(ISO.DATE_TIME, "yyyy-MM-dd'T'HH:mm:ss.SSSZ");
@ -89,6 +89,15 @@ public class DateFormatter implements Formatter<Date> {
this.pattern = pattern; this.pattern = pattern;
} }
/**
* Set the ISO format used for this date.
* @param iso the {@link ISO} format
* @since 3.2
*/
public void setIso(ISO iso) {
this.iso = iso;
}
/** /**
* Set the style to use to format date values. * Set the style to use to format date values.
* <p>If not specified, DateFormat's default style will be used. * <p>If not specified, DateFormat's default style will be used.
@ -112,7 +121,7 @@ public class DateFormatter implements Formatter<Date> {
* <li>'F' = Full</li> * <li>'F' = Full</li>
* <li>'-' = Omitted</li> * <li>'-' = Omitted</li>
* <ul> * <ul>
* This method mimics the styles supported by Joda Time. * This method mimics the styles supported by Joda-Time.
* @param stylePattern two characters from the set {"S", "M", "L", "F", "-"} * @param stylePattern two characters from the set {"S", "M", "L", "F", "-"}
* @since 3.2 * @since 3.2
*/ */
@ -120,14 +129,6 @@ public class DateFormatter implements Formatter<Date> {
this.stylePattern = stylePattern; this.stylePattern = stylePattern;
} }
/**
* Set the ISO format used for this date.
* @param iso the {@link ISO} format
* @since 3.2
*/
public void setIso(ISO iso) {
this.iso = iso;
}
/** /**
* Set the TimeZone to normalize the date values into, if any. * Set the TimeZone to normalize the date values into, if any.
*/ */
@ -204,4 +205,5 @@ public class DateFormatter implements Formatter<Date> {
} }
throw new IllegalStateException("Unsupported style pattern '"+ stylePattern+ "'"); throw new IllegalStateException("Unsupported style pattern '"+ stylePattern+ "'");
} }
} }

View File

@ -27,8 +27,8 @@ import org.springframework.util.Assert;
/** /**
* Configures Date formatting for use with Spring. * Configures Date formatting for use with Spring.
* <p> *
* Designed for direct instantiation but also exposes the static * <p>Designed for direct instantiation but also exposes the static
* {@link #addDateConverters(ConverterRegistry)} utility method for ad hoc use * {@link #addDateConverters(ConverterRegistry)} utility method for ad hoc use
* against any {@code ConverterRegistry} instance. * against any {@code ConverterRegistry} instance.
* *
@ -39,10 +39,20 @@ import org.springframework.util.Assert;
*/ */
public class DateFormatterRegistrar implements FormatterRegistrar { public class DateFormatterRegistrar implements FormatterRegistrar {
private DateFormatter dateFormatter; private DateFormatter dateFormatter;
/**
* Set the date formatter to register. If not specified no formatter is registered.
* This method can be used if global formatter configuration is required.
* @param dateFormatter the date formatter
*/
public void setFormatter(DateFormatter dateFormatter) {
Assert.notNull(dateFormatter, "DateFormatter must not be null");
this.dateFormatter = dateFormatter;
}
public void registerFormatters(FormatterRegistry registry) { public void registerFormatters(FormatterRegistry registry) {
addDateConverters(registry); addDateConverters(registry);
registry.addFormatterForFieldAnnotation(new DateTimeFormatAnnotationFormatterFactory()); registry.addFormatterForFieldAnnotation(new DateTimeFormatAnnotationFormatterFactory());
@ -55,16 +65,6 @@ public class DateFormatterRegistrar implements FormatterRegistrar {
} }
} }
/**
* Set the date formatter to register. If not specified no formatter is registered.
* This method can be used if global formatter configuration is required.
* @param dateFormatter the date formatter
*/
public void setFormatter(DateFormatter dateFormatter) {
Assert.notNull(dateFormatter, "DateFormatter must not be null");
this.dateFormatter = dateFormatter;
}
/** /**
* Add date converters to the specified registry. * Add date converters to the specified registry.
* @param converterRegistry the registry of converters to add to * @param converterRegistry the registry of converters to add to
@ -80,6 +80,7 @@ public class DateFormatterRegistrar implements FormatterRegistrar {
private static class DateToLongConverter implements Converter<Date, Long> { private static class DateToLongConverter implements Converter<Date, Long> {
public Long convert(Date source) { public Long convert(Date source) {
return source.getTime(); return source.getTime();
} }
@ -87,6 +88,7 @@ public class DateFormatterRegistrar implements FormatterRegistrar {
private static class DateToCalendarConverter implements Converter<Date, Calendar> { private static class DateToCalendarConverter implements Converter<Date, Calendar> {
public Calendar convert(Date source) { public Calendar convert(Date source) {
Calendar calendar = Calendar.getInstance(); Calendar calendar = Calendar.getInstance();
calendar.setTime(source); calendar.setTime(source);
@ -96,6 +98,7 @@ public class DateFormatterRegistrar implements FormatterRegistrar {
private static class CalendarToDateConverter implements Converter<Calendar, Date> { private static class CalendarToDateConverter implements Converter<Calendar, Date> {
public Date convert(Calendar source) { public Date convert(Calendar source) {
return source.getTime(); return source.getTime();
} }
@ -103,6 +106,7 @@ public class DateFormatterRegistrar implements FormatterRegistrar {
private static class CalendarToLongConverter implements Converter<Calendar, Long> { private static class CalendarToLongConverter implements Converter<Calendar, Long> {
public Long convert(Calendar source) { public Long convert(Calendar source) {
return source.getTime().getTime(); return source.getTime().getTime();
} }
@ -110,6 +114,7 @@ public class DateFormatterRegistrar implements FormatterRegistrar {
private static class LongToDateConverter implements Converter<Long, Date> { private static class LongToDateConverter implements Converter<Long, Date> {
public Date convert(Long source) { public Date convert(Long source) {
return new Date(source); return new Date(source);
} }
@ -118,10 +123,11 @@ public class DateFormatterRegistrar implements FormatterRegistrar {
private static class LongToCalendarConverter implements Converter<Long, Calendar> { private static class LongToCalendarConverter implements Converter<Long, Calendar> {
private DateToCalendarConverter dateToCalendarConverter = new DateToCalendarConverter(); private final DateToCalendarConverter dateToCalendarConverter = new DateToCalendarConverter();
public Calendar convert(Long source) { public Calendar convert(Long source) {
return this.dateToCalendarConverter.convert(new Date(source)); return this.dateToCalendarConverter.convert(new Date(source));
} }
} }
} }

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2012 the original author or authors. * Copyright 2002-2013 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -44,7 +44,7 @@ public class DateTimeFormatAnnotationFormatterFactory implements
private static final Set<Class<?>> FIELD_TYPES; private static final Set<Class<?>> FIELD_TYPES;
static { static {
Set<Class<?>> fieldTypes = new HashSet<Class<?>>(); Set<Class<?>> fieldTypes = new HashSet<Class<?>>(4);
fieldTypes.add(Date.class); fieldTypes.add(Date.class);
fieldTypes.add(Calendar.class); fieldTypes.add(Calendar.class);
fieldTypes.add(Long.class); fieldTypes.add(Long.class);
@ -82,4 +82,5 @@ public class DateTimeFormatAnnotationFormatterFactory implements
protected String resolveEmbeddedValue(String value) { protected String resolveEmbeddedValue(String value) {
return (this.embeddedValueResolver != null ? this.embeddedValueResolver.resolveStringValue(value) : value); return (this.embeddedValueResolver != null ? this.embeddedValueResolver.resolveStringValue(value) : value);
} }
} }

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2012 the original author or authors. * Copyright 2002-2013 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -22,32 +22,34 @@ import org.joda.time.DateTimeZone;
import org.joda.time.format.DateTimeFormat; import org.joda.time.format.DateTimeFormat;
import org.joda.time.format.DateTimeFormatter; import org.joda.time.format.DateTimeFormatter;
import org.joda.time.format.ISODateTimeFormat; import org.joda.time.format.ISODateTimeFormat;
import org.springframework.format.annotation.DateTimeFormat.ISO; import org.springframework.format.annotation.DateTimeFormat.ISO;
import org.springframework.util.StringUtils; import org.springframework.util.StringUtils;
/** /**
* Factory that creates a Joda {@link DateTimeFormatter}. Formatters will be * Factory that creates a Joda-Time {@link DateTimeFormatter}.
* created using the defined {@link #setPattern(String) pattern}, {@link #setIso ISO}, *
* or {@link #setStyle(String) style} (considered in that order). * <p>Formatters will be created using the defined {@link #setPattern pattern},
* {@link #setIso ISO}, and {@link #setStyle style} methods (considered in that order).
* *
* @author Phillip Webb * @author Phillip Webb
* @author Sam Brannen * @author Sam Brannen
* @since 3.2 * @since 3.2
* @see #createDateTimeFormatter() * @see #createDateTimeFormatter()
* @see #createDateTimeFormatter(DateTimeFormatter) * @see #createDateTimeFormatter(DateTimeFormatter)
* @see #setPattern(String) * @see #setPattern
* @see #setStyle
* @see #setIso * @see #setIso
* @see #setStyle(String)
* @see DateTimeFormatterFactoryBean * @see DateTimeFormatterFactoryBean
*/ */
public class DateTimeFormatterFactory { public class DateTimeFormatterFactory {
private String pattern;
private ISO iso; private ISO iso;
private String style; private String style;
private String pattern;
private TimeZone timeZone; private TimeZone timeZone;
@ -67,9 +69,50 @@ public class DateTimeFormatterFactory {
/** /**
* Create a new {@code DateTimeFormatter} using this factory. If no specific * Set the pattern to use to format date values.
* {@link #setStyle(String) style}, {@link #setIso ISO}, or * @param pattern the format pattern
* {@link #setPattern(String) pattern} have been defined the */
public void setPattern(String pattern) {
this.pattern = pattern;
}
/**
* Set the ISO format used to format date values.
* @param iso the ISO format
*/
public void setIso(ISO iso) {
this.iso = iso;
}
/**
* Set the two characters to use to format date values, in Joda-Time style.
* <p>The first character is used for the date style; the second is for
* the time style. Supported characters are:
* <ul>
* <li>'S' = Small</li>
* <li>'M' = Medium</li>
* <li>'L' = Long</li>
* <li>'F' = Full</li>
* <li>'-' = Omitted</li>
* </ul>
* @param style two characters from the set {"S", "M", "L", "F", "-"}
*/
public void setStyle(String style) {
this.style = style;
}
/**
* Set the {@code TimeZone} to normalize the date values into, if any.
* @param timeZone the time zone
*/
public void setTimeZone(TimeZone timeZone) {
this.timeZone = timeZone;
}
/**
* Create a new {@code DateTimeFormatter} using this factory.
* <p>If no specific pattern or style has been defined,
* {@link DateTimeFormat#mediumDateTime() medium date time format} will be used. * {@link DateTimeFormat#mediumDateTime() medium date time format} will be used.
* @return a new date time formatter * @return a new date time formatter
* @see #createDateTimeFormatter(DateTimeFormatter) * @see #createDateTimeFormatter(DateTimeFormatter)
@ -79,21 +122,20 @@ public class DateTimeFormatterFactory {
} }
/** /**
* Create a new {@code DateTimeFormatter} using this factory. If no specific * Create a new {@code DateTimeFormatter} using this factory.
* {@link #setStyle(String) style}, {@link #setIso ISO}, or * <p>If no specific pattern or style has been defined,
* {@link #setPattern(String) pattern} have been defined the supplied * the supplied {@code fallbackFormatter} will be used.
* {@code fallbackFormatter} will be used. * @param fallbackFormatter the fall-back formatter to use when no specific
* @param fallbackFormatter the fall-back formatter to use when no specific factory * factory properties have been set (can be {@code null}).
* properties have been set (can be {@code null}).
* @return a new date time formatter * @return a new date time formatter
*/ */
public DateTimeFormatter createDateTimeFormatter(DateTimeFormatter fallbackFormatter) { public DateTimeFormatter createDateTimeFormatter(DateTimeFormatter fallbackFormatter) {
DateTimeFormatter dateTimeFormatter = null; DateTimeFormatter dateTimeFormatter = null;
if (StringUtils.hasLength(pattern)) { if (StringUtils.hasLength(this.pattern)) {
dateTimeFormatter = DateTimeFormat.forPattern(pattern); dateTimeFormatter = DateTimeFormat.forPattern(this.pattern);
} }
else if (iso != null && iso != ISO.NONE) { else if (this.iso != null && this.iso != ISO.NONE) {
switch (iso) { switch (this.iso) {
case DATE: case DATE:
dateTimeFormatter = ISODateTimeFormat.date(); dateTimeFormatter = ISODateTimeFormat.date();
break; break;
@ -107,11 +149,11 @@ public class DateTimeFormatterFactory {
/* no-op */ /* no-op */
break; break;
default: default:
throw new IllegalStateException("Unsupported ISO format: " + iso); throw new IllegalStateException("Unsupported ISO format: " + this.iso);
} }
} }
else if (StringUtils.hasLength(style)) { else if (StringUtils.hasLength(this.style)) {
dateTimeFormatter = DateTimeFormat.forStyle(style); dateTimeFormatter = DateTimeFormat.forStyle(this.style);
} }
if (dateTimeFormatter != null && this.timeZone != null) { if (dateTimeFormatter != null && this.timeZone != null) {
@ -120,44 +162,4 @@ public class DateTimeFormatterFactory {
return (dateTimeFormatter != null ? dateTimeFormatter : fallbackFormatter); return (dateTimeFormatter != null ? dateTimeFormatter : fallbackFormatter);
} }
/**
* Set the {@code TimeZone} to normalize the date values into, if any.
* @param timeZone the time zone
*/
public void setTimeZone(TimeZone timeZone) {
this.timeZone = timeZone;
}
/**
* Set the two characters to use to format date values. The first character is used for
* the date style; the second is for the time style. Supported characters are:
* <ul>
* <li>'S' = Small</li>
* <li>'M' = Medium</li>
* <li>'L' = Long</li>
* <li>'F' = Full</li>
* <li>'-' = Omitted</li>
* </ul>
* <p>This method mimics the styles supported by Joda Time.
* @param style two characters from the set {"S", "M", "L", "F", "-"}
*/
public void setStyle(String style) {
this.style = style;
}
/**
* Set the ISO format used to format date values.
* @param iso the ISO format
*/
public void setIso(ISO iso) {
this.iso = iso;
}
/**
* Set the pattern to use to format date values.
* @param pattern the format pattern
*/
public void setPattern(String pattern) {
this.pattern = pattern;
}
} }

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2012 the original author or authors. * Copyright 2002-2013 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -22,15 +22,15 @@ import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.InitializingBean; import org.springframework.beans.factory.InitializingBean;
/** /**
* {@link FactoryBean} that creates a Joda {@link DateTimeFormatter}. See the * {@link FactoryBean} that creates a Joda-Time {@link DateTimeFormatter}.
* {@linkplain DateTimeFormatterFactory base class} for configuration details. * See the {@link DateTimeFormatterFactory base class} for configuration details.
* *
* @author Phillip Webb * @author Phillip Webb
* @author Sam Brannen * @author Sam Brannen
* @since 3.2 * @since 3.2
* @see #setPattern(String) * @see #setPattern
* @see #setIso(org.springframework.format.annotation.DateTimeFormat.ISO) * @see #setIso
* @see #setStyle(String) * @see #setStyle
* @see DateTimeFormatterFactory * @see DateTimeFormatterFactory
*/ */
public class DateTimeFormatterFactoryBean extends DateTimeFormatterFactory public class DateTimeFormatterFactoryBean extends DateTimeFormatterFactory

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2012 the original author or authors. * Copyright 2002-2013 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -56,7 +56,7 @@ public class JodaDateTimeFormatAnnotationFormatterFactory
// (if we did not do this, the default byType rules for LocalDate, LocalTime, // (if we did not do this, the default byType rules for LocalDate, LocalTime,
// and LocalDateTime would take precedence over the annotation rule, which // and LocalDateTime would take precedence over the annotation rule, which
// is not what we want) // is not what we want)
Set<Class<?>> fieldTypes = new HashSet<Class<?>>(7); Set<Class<?>> fieldTypes = new HashSet<Class<?>>(8);
fieldTypes.add(ReadableInstant.class); fieldTypes.add(ReadableInstant.class);
fieldTypes.add(LocalDate.class); fieldTypes.add(LocalDate.class);
fieldTypes.add(LocalTime.class); fieldTypes.add(LocalTime.class);
@ -71,10 +71,6 @@ public class JodaDateTimeFormatAnnotationFormatterFactory
private StringValueResolver embeddedValueResolver; private StringValueResolver embeddedValueResolver;
public final Set<Class<?>> getFieldTypes() {
return FIELD_TYPES;
}
public void setEmbeddedValueResolver(StringValueResolver resolver) { public void setEmbeddedValueResolver(StringValueResolver resolver) {
this.embeddedValueResolver = resolver; this.embeddedValueResolver = resolver;
} }
@ -83,21 +79,25 @@ public class JodaDateTimeFormatAnnotationFormatterFactory
return (this.embeddedValueResolver != null ? this.embeddedValueResolver.resolveStringValue(value) : value); return (this.embeddedValueResolver != null ? this.embeddedValueResolver.resolveStringValue(value) : value);
} }
public final Set<Class<?>> getFieldTypes() {
return FIELD_TYPES;
}
public Printer<?> getPrinter(DateTimeFormat annotation, Class<?> fieldType) { public Printer<?> getPrinter(DateTimeFormat annotation, Class<?> fieldType) {
DateTimeFormatter formatter = getFormatter(annotation, fieldType); DateTimeFormatter formatter = getFormatter(annotation, fieldType);
if (ReadableInstant.class.isAssignableFrom(fieldType)) {
return new ReadableInstantPrinter(formatter);
}
if (ReadablePartial.class.isAssignableFrom(fieldType)) { if (ReadablePartial.class.isAssignableFrom(fieldType)) {
return new ReadablePartialPrinter(formatter); return new ReadablePartialPrinter(formatter);
} }
if (Calendar.class.isAssignableFrom(fieldType)) { else if (ReadableInstant.class.isAssignableFrom(fieldType) || Calendar.class.isAssignableFrom(fieldType)) {
// assumes Calendar->ReadableInstant converter is registered // assumes Calendar->ReadableInstant converter is registered
return new ReadableInstantPrinter(formatter); return new ReadableInstantPrinter(formatter);
} }
else {
// assumes Date->Long converter is registered // assumes Date->Long converter is registered
return new MillisecondInstantPrinter(formatter); return new MillisecondInstantPrinter(formatter);
} }
}
public Parser<DateTime> getParser(DateTimeFormat annotation, Class<?> fieldType) { public Parser<DateTime> getParser(DateTimeFormat annotation, Class<?> fieldType) {
return new DateTimeParser(getFormatter(annotation, fieldType)); return new DateTimeParser(getFormatter(annotation, fieldType));

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2012 the original author or authors. * Copyright 2002-2013 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -21,8 +21,10 @@ import org.joda.time.DateTimeZone;
import org.joda.time.format.DateTimeFormatter; import org.joda.time.format.DateTimeFormatter;
/** /**
* A context that holds user-specific Joda Time settings such as the user's Chronology (calendar system) and time zone. * A context that holds user-specific Joda-Time settings such as the user's
* A {@code null} property value indicate the user has not specified a setting. * Chronology (calendar system) and time zone.
*
* <p>A {@code null} property value indicate the user has not specified a setting.
* *
* @author Keith Donald * @author Keith Donald
* @since 3.0 * @since 3.0
@ -43,8 +45,7 @@ public class JodaTimeContext {
} }
/** /**
* The user's chronology (calendar system). * The user's chronology (calendar system), if any.
* Null if not specified.
*/ */
public Chronology getChronology() { public Chronology getChronology() {
return this.chronology; return this.chronology;
@ -58,8 +59,7 @@ public class JodaTimeContext {
} }
/** /**
* The user's timezone. * The user's timezone, if any.
* Null if not specified.
*/ */
public DateTimeZone getTimeZone() { public DateTimeZone getTimeZone() {
return timeZone; return timeZone;
@ -67,9 +67,11 @@ public class JodaTimeContext {
/** /**
* Gets the Formatter with the this context's settings applied to the base {@code formatter}. * Get the DateTimeFormatter with the this context's settings
* @param formatter the base formatter that establishes default formatting rules, generally context independent * applied to the base {@code formatter}.
* @return the context DateTimeFormatter * @param formatter the base formatter that establishes default
* formatting rules, generally context-independent
* @return the contextual DateTimeFormatter
*/ */
public DateTimeFormatter getFormatter(DateTimeFormatter formatter) { public DateTimeFormatter getFormatter(DateTimeFormatter formatter) {
if (this.chronology != null) { if (this.chronology != null) {

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2012 the original author or authors. * Copyright 2002-2013 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -23,7 +23,8 @@ import org.joda.time.format.DateTimeFormatter;
import org.springframework.core.NamedThreadLocal; import org.springframework.core.NamedThreadLocal;
/** /**
* A holder for a thread-local user {@link JodaTimeContext}. * A holder for a thread-local {@link JodaTimeContext}
* with user-specific Joda-Time settings.
* *
* @author Keith Donald * @author Keith Donald
* @author Juergen Hoeller * @author Juergen Hoeller

View File

@ -27,12 +27,14 @@ import org.joda.time.LocalDateTime;
import org.joda.time.LocalTime; import org.joda.time.LocalTime;
import org.joda.time.MutableDateTime; import org.joda.time.MutableDateTime;
import org.joda.time.ReadableInstant; import org.joda.time.ReadableInstant;
import org.springframework.core.convert.converter.Converter; import org.springframework.core.convert.converter.Converter;
import org.springframework.core.convert.converter.ConverterRegistry; import org.springframework.core.convert.converter.ConverterRegistry;
import org.springframework.format.datetime.DateFormatterRegistrar; import org.springframework.format.datetime.DateFormatterRegistrar;
/** /**
* Installs lower-level type converters required to integrate Joda Time support into Spring's field formatting system. * Installs lower-level type converters required to integrate
* Joda-Time support into Spring's field formatting system.
* *
* @author Keith Donald * @author Keith Donald
* @author Phillip Webb * @author Phillip Webb
@ -50,116 +52,87 @@ final class JodaTimeConverters {
registry.addConverter(new DateTimeToLocalTimeConverter()); registry.addConverter(new DateTimeToLocalTimeConverter());
registry.addConverter(new DateTimeToLocalDateTimeConverter()); registry.addConverter(new DateTimeToLocalDateTimeConverter());
registry.addConverter(new DateTimeToDateMidnightConverter()); registry.addConverter(new DateTimeToDateMidnightConverter());
registry.addConverter(new DateTimeToInstantConverter());
registry.addConverter(new DateTimeToMutableDateTimeConverter()); registry.addConverter(new DateTimeToMutableDateTimeConverter());
registry.addConverter(new DateTimeToInstantConverter());
registry.addConverter(new DateTimeToDateConverter()); registry.addConverter(new DateTimeToDateConverter());
registry.addConverter(new DateTimeToCalendarConverter()); registry.addConverter(new DateTimeToCalendarConverter());
registry.addConverter(new DateTimeToLongConverter()); registry.addConverter(new DateTimeToLongConverter());
registry.addConverter(new CalendarToReadableInstantConverter());
registry.addConverter(new DateToReadableInstantConverter()); registry.addConverter(new DateToReadableInstantConverter());
registry.addConverter(new CalendarToReadableInstantConverter());
} }
/**
* Used when binding a parsed DateTime to a LocalDate field.
* @see DateTimeParser
**/
private static class DateTimeToLocalDateConverter implements Converter<DateTime, LocalDate> { private static class DateTimeToLocalDateConverter implements Converter<DateTime, LocalDate> {
public LocalDate convert(DateTime source) { public LocalDate convert(DateTime source) {
return source.toLocalDate(); return source.toLocalDate();
} }
} }
/**
* Used when binding a parsed DateTime to a LocalTime field.
* @see DateTimeParser
*/
private static class DateTimeToLocalTimeConverter implements Converter<DateTime, LocalTime> { private static class DateTimeToLocalTimeConverter implements Converter<DateTime, LocalTime> {
public LocalTime convert(DateTime source) { public LocalTime convert(DateTime source) {
return source.toLocalTime(); return source.toLocalTime();
} }
} }
/**
* Used when binding a parsed DateTime to a LocalDateTime field.
* @see DateTimeParser
*/
private static class DateTimeToLocalDateTimeConverter implements Converter<DateTime, LocalDateTime> { private static class DateTimeToLocalDateTimeConverter implements Converter<DateTime, LocalDateTime> {
public LocalDateTime convert(DateTime source) { public LocalDateTime convert(DateTime source) {
return source.toLocalDateTime(); return source.toLocalDateTime();
} }
} }
/**
* Used when binding a parsed DateTime to a DateMidnight field.
* @see DateTimeParser
*/
private static class DateTimeToDateMidnightConverter implements Converter<DateTime, DateMidnight> { private static class DateTimeToDateMidnightConverter implements Converter<DateTime, DateMidnight> {
public DateMidnight convert(DateTime source) { public DateMidnight convert(DateTime source) {
return source.toDateMidnight(); return source.toDateMidnight();
} }
} }
/**
* Used when binding a parsed DateTime to an Instant field.
* @see DateTimeParser
*/
private static class DateTimeToInstantConverter implements Converter<DateTime, Instant> {
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<DateTime, MutableDateTime> { private static class DateTimeToMutableDateTimeConverter implements Converter<DateTime, MutableDateTime> {
public MutableDateTime convert(DateTime source) { public MutableDateTime convert(DateTime source) {
return source.toMutableDateTime(); return source.toMutableDateTime();
} }
} }
/**
* Used when binding a parsed DateTime to a java.util.Date field. private static class DateTimeToInstantConverter implements Converter<DateTime, Instant> {
* @see DateTimeParser
*/ public Instant convert(DateTime source) {
return source.toInstant();
}
}
private static class DateTimeToDateConverter implements Converter<DateTime, Date> { private static class DateTimeToDateConverter implements Converter<DateTime, Date> {
public Date convert(DateTime source) { public Date convert(DateTime source) {
return source.toDate(); return source.toDate();
} }
} }
/**
* Used when binding a parsed DateTime to a java.util.Calendar field.
* @see DateTimeParser
*/
private static class DateTimeToCalendarConverter implements Converter<DateTime, Calendar> { private static class DateTimeToCalendarConverter implements Converter<DateTime, Calendar> {
public Calendar convert(DateTime source) { public Calendar convert(DateTime source) {
return source.toGregorianCalendar(); return source.toGregorianCalendar();
} }
} }
/**
* Used when binding a parsed DateTime to a java.lang.Long field.
* @see DateTimeParser
*/
private static class DateTimeToLongConverter implements Converter<DateTime, Long> { private static class DateTimeToLongConverter implements Converter<DateTime, Long> {
public Long convert(DateTime source) { public Long convert(DateTime source) {
return source.getMillis(); return source.getMillis();
} }
} }
/**
* Used when printing a java.util.Calendar field with a ReadableInstantPrinter.
* @see MillisecondInstantPrinter
* @see JodaDateTimeFormatAnnotationFormatterFactory
*/
private static class CalendarToReadableInstantConverter implements Converter<Calendar, ReadableInstant> {
public ReadableInstant convert(Calendar source) {
return new DateTime(source);
}
}
/** /**
* Used when printing a java.util.Date field with a ReadableInstantPrinter. * Used when printing a java.util.Date field with a ReadableInstantPrinter.
@ -167,8 +140,23 @@ final class JodaTimeConverters {
* @see JodaDateTimeFormatAnnotationFormatterFactory * @see JodaDateTimeFormatAnnotationFormatterFactory
*/ */
private static class DateToReadableInstantConverter implements Converter<Date, ReadableInstant> { private static class DateToReadableInstantConverter implements Converter<Date, ReadableInstant> {
public ReadableInstant convert(Date source) { public ReadableInstant convert(Date source) {
return new DateTime(source); return new DateTime(source);
} }
} }
/**
* Used when printing a java.util.Calendar field with a ReadableInstantPrinter.
* @see MillisecondInstantPrinter
* @see JodaDateTimeFormatAnnotationFormatterFactory
*/
private static class CalendarToReadableInstantConverter implements Converter<Calendar, ReadableInstant> {
public ReadableInstant convert(Calendar source) {
return new DateTime(source);
}
}
} }

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2012 the original author or authors. * Copyright 2002-2013 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package org.springframework.format.datetime.joda; package org.springframework.format.datetime.joda;
import java.util.Calendar; import java.util.Calendar;
@ -35,7 +36,7 @@ import org.springframework.format.Printer;
import org.springframework.format.annotation.DateTimeFormat.ISO; import org.springframework.format.annotation.DateTimeFormat.ISO;
/** /**
* Configures Joda Time's formatting system for use with Spring. * Configures Joda-Time's formatting system for use with Spring.
* *
* @author Keith Donald * @author Keith Donald
* @author Juergen Hoeller * @author Juergen Hoeller
@ -51,15 +52,18 @@ import org.springframework.format.annotation.DateTimeFormat.ISO;
*/ */
public class JodaTimeFormatterRegistrar implements FormatterRegistrar { public class JodaTimeFormatterRegistrar implements FormatterRegistrar {
private static enum Type {DATE, TIME, DATE_TIME}
/** /**
* User defined formatters. * User defined formatters.
*/ */
private Map<Type, DateTimeFormatter> formatters = new HashMap<Type, DateTimeFormatter>(); private final Map<Type, DateTimeFormatter> formatters = new HashMap<Type, DateTimeFormatter>();
/** /**
* Factories used when specific formatters have not been specified. * Factories used when specific formatters have not been specified.
*/ */
private Map<Type, DateTimeFormatterFactory> factories; private final Map<Type, DateTimeFormatterFactory> factories;
public JodaTimeFormatterRegistrar() { public JodaTimeFormatterRegistrar() {
@ -70,6 +74,18 @@ public class JodaTimeFormatterRegistrar implements FormatterRegistrar {
} }
/**
* Set whether standard ISO formatting should be applied to all date/time types.
* Default is "false" (no).
* <p>If set to "true", the "dateStyle", "timeStyle" and "dateTimeStyle"
* properties are effectively ignored.
*/
public void setUseIsoFormat(boolean useIsoFormat) {
this.factories.get(Type.DATE).setIso(useIsoFormat ? ISO.DATE : null);
this.factories.get(Type.TIME).setIso(useIsoFormat ? ISO.TIME : null);
this.factories.get(Type.DATE_TIME).setIso(useIsoFormat ? ISO.DATE_TIME : null);
}
/** /**
* Set the default format style of Joda {@link LocalDate} objects. * Set the default format style of Joda {@link LocalDate} objects.
* Default is {@link DateTimeFormat#shortDate()}. * Default is {@link DateTimeFormat#shortDate()}.
@ -95,25 +111,14 @@ public class JodaTimeFormatterRegistrar implements FormatterRegistrar {
this.factories.get(Type.DATE_TIME).setStyle(dateTimeStyle); this.factories.get(Type.DATE_TIME).setStyle(dateTimeStyle);
} }
/**
* Set whether standard ISO formatting should be applied to all Date/Time types.
* Default is false (no).
* If set to true, the dateStyle, timeStyle, and dateTimeStyle properties are ignored.
*/
public void setUseIsoFormat(boolean useIsoFormat) {
this.factories.get(Type.DATE).setIso(useIsoFormat ? ISO.DATE : null);
this.factories.get(Type.TIME).setIso(useIsoFormat ? ISO.TIME : null);
this.factories.get(Type.DATE_TIME).setIso(useIsoFormat ? ISO.DATE_TIME : null);
}
/** /**
* Set the formatter that will be used for objects representing date values. * Set the formatter that will be used for objects representing date values.
* <p>This formatter will be used for the {@link LocalDate} type. When specified * <p>This formatter will be used for the {@link LocalDate} type. When specified
* the {@link #setDateStyle(String) dateStyle} and * the {@link #setDateStyle(String) dateStyle} and
* {@link #setUseIsoFormat(boolean) useIsoFormat} properties will be ignored. * {@link #setUseIsoFormat(boolean) useIsoFormat} properties will be ignored.
* @param formatter the formatter to use * @param formatter the formatter to use
* @see #setTimeFormatter(DateTimeFormatter) * @see #setTimeFormatter
* @see #setDateTimeFormatter(DateTimeFormatter) * @see #setDateTimeFormatter
* @since 3.2 * @since 3.2
*/ */
public void setDateFormatter(DateTimeFormatter formatter) { public void setDateFormatter(DateTimeFormatter formatter) {
@ -126,8 +131,8 @@ public class JodaTimeFormatterRegistrar implements FormatterRegistrar {
* the {@link #setTimeStyle(String) timeStyle} and * the {@link #setTimeStyle(String) timeStyle} and
* {@link #setUseIsoFormat(boolean) useIsoFormat} properties will be ignored. * {@link #setUseIsoFormat(boolean) useIsoFormat} properties will be ignored.
* @param formatter the formatter to use * @param formatter the formatter to use
* @see #setDateFormatter(DateTimeFormatter) * @see #setDateFormatter
* @see #setDateTimeFormatter(DateTimeFormatter) * @see #setDateTimeFormatter
* @since 3.2 * @since 3.2
*/ */
public void setTimeFormatter(DateTimeFormatter formatter) { public void setTimeFormatter(DateTimeFormatter formatter) {
@ -141,14 +146,15 @@ public class JodaTimeFormatterRegistrar implements FormatterRegistrar {
* the {@link #setDateTimeStyle(String) dateTimeStyle} and * the {@link #setDateTimeStyle(String) dateTimeStyle} and
* {@link #setUseIsoFormat(boolean) useIsoFormat} properties will be ignored. * {@link #setUseIsoFormat(boolean) useIsoFormat} properties will be ignored.
* @param formatter the formatter to use * @param formatter the formatter to use
* @see #setDateFormatter(DateTimeFormatter) * @see #setDateFormatter
* @see #setTimeFormatter(DateTimeFormatter) * @see #setTimeFormatter
* @since 3.2 * @since 3.2
*/ */
public void setDateTimeFormatter(DateTimeFormatter formatter) { public void setDateTimeFormatter(DateTimeFormatter formatter) {
this.formatters.put(Type.DATE_TIME, formatter); this.formatters.put(Type.DATE_TIME, formatter);
} }
public void registerFormatters(FormatterRegistry registry) { public void registerFormatters(FormatterRegistry registry) {
JodaTimeConverters.registerConverters(registry); JodaTimeConverters.registerConverters(registry);
@ -176,7 +182,7 @@ public class JodaTimeFormatterRegistrar implements FormatterRegistrar {
new DateTimeParser(dateTimeFormatter), new DateTimeParser(dateTimeFormatter),
ReadableInstant.class); ReadableInstant.class);
// In order to retain back compatibility we only register Date/Calendar // In order to retain backwards compatibility we only register Date/Calendar
// types when a user defined formatter is specified (see SPR-10105) // types when a user defined formatter is specified (see SPR-10105)
if( this.formatters.containsKey(Type.DATE_TIME)) { if( this.formatters.containsKey(Type.DATE_TIME)) {
addFormatterForFields(registry, addFormatterForFields(registry,
@ -185,8 +191,7 @@ public class JodaTimeFormatterRegistrar implements FormatterRegistrar {
Date.class, Calendar.class); Date.class, Calendar.class);
} }
registry.addFormatterForFieldAnnotation( registry.addFormatterForFieldAnnotation(new JodaDateTimeFormatAnnotationFormatterFactory());
new JodaDateTimeFormatAnnotationFormatterFactory());
} }
private DateTimeFormatter getFormatter(Type type) { private DateTimeFormatter getFormatter(Type type) {
@ -208,10 +213,10 @@ public class JodaTimeFormatterRegistrar implements FormatterRegistrar {
private void addFormatterForFields(FormatterRegistry registry, Printer<?> printer, private void addFormatterForFields(FormatterRegistry registry, Printer<?> printer,
Parser<?> parser, Class<?>... fieldTypes) { Parser<?> parser, Class<?>... fieldTypes) {
for (Class<?> fieldType : fieldTypes) { for (Class<?> fieldType : fieldTypes) {
registry.addFormatterForFieldType(fieldType, printer, parser); registry.addFormatterForFieldType(fieldType, printer, parser);
} }
} }
private static enum Type {DATE, TIME, DATE_TIME}
} }

View File

@ -1,4 +1,4 @@
/** /**
* Integration with the Joda Time for formatting Joda types as well as standard JDK Date types. * Integration with Joda-Time for formatting Joda date and time types as well as standard JDK Date types.
*/ */
package org.springframework.format.datetime.joda; package org.springframework.format.datetime.joda;

View File

@ -26,8 +26,6 @@ import org.joda.time.format.DateTimeFormatter;
import org.junit.Test; import org.junit.Test;
/** /**
* Tests for {@link DateTimeFormatterFactoryBean}.
*
* @author Phillip Webb * @author Phillip Webb
* @author Sam Brannen * @author Sam Brannen
*/ */
@ -60,4 +58,5 @@ public class DateTimeFormatterFactoryBeanTests {
factory.setStyle("LL"); factory.setStyle("LL");
assertThat(factory.getObject(), is(sameInstance(formatter))); assertThat(factory.getObject(), is(sameInstance(formatter)));
} }
} }

View File

@ -30,8 +30,6 @@ import org.junit.Test;
import org.springframework.format.annotation.DateTimeFormat.ISO; import org.springframework.format.annotation.DateTimeFormat.ISO;
/** /**
* Tests for {@link DateTimeFormatterFactory}.
*
* @author Phillip Webb * @author Phillip Webb
* @author Sam Brannen * @author Sam Brannen
*/ */
@ -100,4 +98,5 @@ public class DateTimeFormatterFactoryTests {
private DateTimeFormatter applyLocale(DateTimeFormatter dateTimeFormatter) { private DateTimeFormatter applyLocale(DateTimeFormatter dateTimeFormatter) {
return dateTimeFormatter.withLocale(Locale.US); return dateTimeFormatter.withLocale(Locale.US);
} }
} }

View File

@ -116,7 +116,7 @@ public class JodaTimeFormattingTests {
} }
@Test @Test
public void testBindLocalDateWithSpecifcFormatter() throws Exception { public void testBindLocalDateWithSpecificFormatter() throws Exception {
JodaTimeFormatterRegistrar registrar = new JodaTimeFormatterRegistrar(); JodaTimeFormatterRegistrar registrar = new JodaTimeFormatterRegistrar();
registrar.setDateFormatter(org.joda.time.format.DateTimeFormat.forPattern("yyyyMMdd")); registrar.setDateFormatter(org.joda.time.format.DateTimeFormat.forPattern("yyyyMMdd"));
setUp(registrar); setUp(registrar);