diff --git a/build.gradle b/build.gradle
index 6d97c7be17..aa9b657402 100644
--- a/build.gradle
+++ b/build.gradle
@@ -262,12 +262,12 @@ project('spring-context') {
}
compile("org.beanshell:bsh:2.0b4", optional)
compile("org.codehaus.groovy:groovy-all:1.6.3", optional)
- compile("org.jruby:jruby:1.4.0", optional)
compile("org.hibernate:hibernate-validator:4.2.0.Final") { dep ->
optional dep
exclude group: 'org.slf4j', module: 'slf4j-api'
}
compile("joda-time:joda-time:1.6", optional)
+ compile("org.jruby:jruby:1.4.0", optional)
compile("javax.cache:cache-api:0.5", optional)
compile("net.sf.ehcache:ehcache-core:2.0.0", optional)
compile("org.slf4j:slf4j-api:1.6.1", optional)
diff --git a/spring-context/src/main/java/org/springframework/format/annotation/DateTimeFormat.java b/spring-context/src/main/java/org/springframework/format/annotation/DateTimeFormat.java
index 8646fa3415..c5e526e8e0 100644
--- a/spring-context/src/main/java/org/springframework/format/annotation/DateTimeFormat.java
+++ b/spring-context/src/main/java/org/springframework/format/annotation/DateTimeFormat.java
@@ -26,7 +26,7 @@ import java.lang.annotation.Target;
* Supports formatting by style pattern, ISO date time pattern, or custom format pattern string.
* Can be applied to java.util.Date, java.util.Calendar, java.long.Long, or Joda Time fields.
*
- * For style-based formatting, set the {@link #style()} attribute to be the style pattern code.
+ * 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.
* 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 '-'.
@@ -39,7 +39,7 @@ import java.lang.annotation.Target;
* 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 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 Juergen Hoeller
* @since 3.0
@@ -76,23 +76,23 @@ public @interface DateTimeFormat {
* Common ISO date time format patterns.
*/
public enum ISO {
-
- /**
+
+ /**
* The most common ISO Date Format yyyy-MM-dd e.g. 2000-10-31.
*/
DATE,
- /**
- * The most common ISO Time Format hh:mm:ss.SSSZ e.g. 01:30:00.000-05:00.
+ /**
+ * The most common ISO Time Format HH:mm:ss.SSSZ e.g. 01:30:00.000-05:00.
*/
TIME,
- /**
- * The most common ISO DateTime Format 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 yyyy-MM-dd'T'HH:mm:ss.SSSZ e.g. 2000-10-31 01:30:00.000-05:00.
* The default if no annotation value is specified.
*/
DATE_TIME,
-
+
/**
* Indicates that no ISO-based format pattern should be applied.
*/
diff --git a/spring-context/src/main/java/org/springframework/format/datetime/DateFormatter.java b/spring-context/src/main/java/org/springframework/format/datetime/DateFormatter.java
index a371b4cf90..365800aba8 100644
--- a/spring-context/src/main/java/org/springframework/format/datetime/DateFormatter.java
+++ b/spring-context/src/main/java/org/springframework/format/datetime/DateFormatter.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2009 the original author or authors.
+ * Copyright 2002-2012 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.
@@ -19,11 +19,18 @@ package org.springframework.format.datetime;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
+import java.util.Collections;
import java.util.Date;
+import java.util.HashMap;
import java.util.Locale;
+import java.util.Map;
import java.util.TimeZone;
import org.springframework.format.Formatter;
+import org.springframework.format.annotation.DateTimeFormat;
+import org.springframework.format.annotation.DateTimeFormat.ISO;
+import org.springframework.util.Assert;
+import org.springframework.util.StringUtils;
/**
* A formatter for {@link java.util.Date} types.
@@ -31,15 +38,30 @@ import org.springframework.format.Formatter;
*
* @author Keith Donald
* @author Juergen Hoeller
+ * @author Phillip Webb
* @since 3.0
- * @see SimpleDateFormat
+ * @see SimpleDateFormat
*/
public class DateFormatter implements Formatter {
+ private static final Map ISO_PATTERNS;
+ static {
+ Map formats = new HashMap();
+ formats.put(ISO.DATE, "yyyy-MM-dd");
+ formats.put(ISO.TIME, "HH:mm:ss.SSSZ");
+ formats.put(ISO.DATE_TIME, "yyyy-MM-dd'T'HH:mm:ss.SSSZ");
+ ISO_PATTERNS = Collections.unmodifiableMap(formats);
+ }
+
+
private String pattern;
private int style = DateFormat.DEFAULT;
+ private String stylePattern;
+
+ private ISO iso;
+
private TimeZone timeZone;
private boolean lenient = false;
@@ -80,6 +102,32 @@ public class DateFormatter implements Formatter {
this.style = style;
}
+ /**
+ * Set the two character to use to format date values. The first character used for
+ * the date style, the second is for the time style. Supported characters are
+ *
+ * - 'S' = Small
+ * - 'M' = Medium
+ * - 'L' = Long
+ * - 'F' = Full
+ * - '-' = Omitted
+ *
+ * This method mimics the styles supported by Joda Time.
+ * @param stylePattern two characters from the set {"S", "M", "L", "F", "-"}
+ * @since 3.2
+ */
+ public void setStylePattern(String 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.
*/
@@ -107,13 +155,7 @@ public class DateFormatter implements Formatter {
protected DateFormat getDateFormat(Locale locale) {
- DateFormat dateFormat;
- if (this.pattern != null) {
- dateFormat = new SimpleDateFormat(this.pattern, locale);
- }
- else {
- dateFormat = DateFormat.getDateInstance(this.style, locale);
- }
+ DateFormat dateFormat = createDateFormat(locale);
if (this.timeZone != null) {
dateFormat.setTimeZone(this.timeZone);
}
@@ -121,4 +163,45 @@ public class DateFormatter implements Formatter {
return dateFormat;
}
+ private DateFormat createDateFormat(Locale locale) {
+ if (StringUtils.hasLength(this.pattern)) {
+ return new SimpleDateFormat(this.pattern, locale);
+ }
+ if (iso != null && iso != ISO.NONE) {
+ String pattern = ISO_PATTERNS.get(iso);
+ Assert.state(pattern != null, "Unsupported ISO format " + iso);
+ SimpleDateFormat format = new SimpleDateFormat(pattern);
+ format.setTimeZone(TimeZone.getTimeZone("UTC"));
+ return format;
+ }
+ if(StringUtils.hasLength(stylePattern)) {
+ int dateStyle = getStylePatternForChar(0);
+ int timeStyle = getStylePatternForChar(1);
+ if(dateStyle != -1 && timeStyle != -1) {
+ return DateFormat.getDateTimeInstance(dateStyle, timeStyle, locale);
+ }
+ if(dateStyle != -1) {
+ return DateFormat.getDateInstance(dateStyle, locale);
+ }
+ if(timeStyle != -1) {
+ return DateFormat.getTimeInstance(timeStyle, locale);
+ }
+ throw new IllegalStateException("Unsupported style pattern '"+ stylePattern+ "'");
+
+ }
+ return DateFormat.getDateInstance(this.style, locale);
+ }
+
+ private int getStylePatternForChar(int index) {
+ if(stylePattern != null && stylePattern.length() > index) {
+ switch (stylePattern.charAt(index)) {
+ case 'S': return DateFormat.SHORT;
+ case 'M': return DateFormat.MEDIUM;
+ case 'L': return DateFormat.LONG;
+ case 'F': return DateFormat.FULL;
+ case '-': return -1;
+ }
+ }
+ throw new IllegalStateException("Unsupported style pattern '"+ stylePattern+ "'");
+ }
}
diff --git a/spring-context/src/main/java/org/springframework/format/datetime/DateFormatterRegistrar.java b/spring-context/src/main/java/org/springframework/format/datetime/DateFormatterRegistrar.java
new file mode 100644
index 0000000000..66584ea56c
--- /dev/null
+++ b/spring-context/src/main/java/org/springframework/format/datetime/DateFormatterRegistrar.java
@@ -0,0 +1,124 @@
+/*
+ * Copyright 2002-2012 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;
+
+import java.util.Calendar;
+import java.util.Date;
+
+import org.springframework.core.convert.converter.Converter;
+import org.springframework.core.convert.converter.ConverterRegistry;
+import org.springframework.format.FormatterRegistrar;
+import org.springframework.format.FormatterRegistry;
+import org.springframework.format.datetime.joda.JodaTimeFormatterRegistrar;
+import org.springframework.util.Assert;
+
+/**
+ * Configures Date formatting for use with Spring.
+ *
+ * Designed for direct instantiation but also exposes the static
+ * {@link #addDateConverters(ConverterRegistry)} utility method for ad hoc use against any
+ * {@code ConverterRegistry} instance.
+ *
+ * @author Phillip Webb
+ * @since 3.2
+ * @see JodaTimeFormatterRegistrar
+ * @see FormatterRegistrar#registerFormatters
+ */
+public class DateFormatterRegistrar implements FormatterRegistrar {
+
+
+ private DateFormatter dateFormatter = new DateFormatter();
+
+
+ public void registerFormatters(FormatterRegistry registry) {
+ addDateConverters(registry);
+ registry.addFormatter(dateFormatter);
+ registry.addFormatterForFieldType(Calendar.class, dateFormatter);
+ registry.addFormatterForFieldAnnotation(new DateTimeFormatAnnotationFormatterFactory());
+ }
+
+ /**
+ * Set the date formatter to register. If not specified default {@link DateFormatter}
+ * will be used. This method can be used if additional 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.
+ * @param converterRegistry the registry of converters to add to
+ */
+ public static void addDateConverters(ConverterRegistry converterRegistry) {
+ converterRegistry.addConverter(new DateToLongConverter());
+ converterRegistry.addConverter(new DateToCalendarConverter());
+ converterRegistry.addConverter(new CalendarToDateConverter());
+ converterRegistry.addConverter(new CalendarToLongConverter());
+ converterRegistry.addConverter(new LongToDateConverter());
+ converterRegistry.addConverter(new LongToCalendarConverter());
+ }
+
+
+ private static class DateToLongConverter implements Converter {
+ public Long convert(Date source) {
+ return source.getTime();
+ }
+ }
+
+
+ private static class DateToCalendarConverter implements Converter {
+ public Calendar convert(Date source) {
+ Calendar calendar = Calendar.getInstance();
+ calendar.setTime(source);
+ return calendar;
+ }
+ }
+
+
+ private static class CalendarToDateConverter implements Converter {
+ public Date convert(Calendar source) {
+ return source.getTime();
+ }
+ }
+
+
+ private static class CalendarToLongConverter implements Converter {
+ public Long convert(Calendar source) {
+ return source.getTime().getTime();
+ }
+ }
+
+
+ private static class LongToDateConverter implements Converter {
+ public Date convert(Long source) {
+ return new Date(source);
+ }
+ }
+
+
+ private static class LongToCalendarConverter implements Converter {
+
+ private DateToCalendarConverter dateToCalendarConverter = new DateToCalendarConverter();
+
+ public Calendar convert(Long source) {
+ return dateToCalendarConverter.convert(new Date(source));
+ }
+ }
+}
diff --git a/spring-context/src/main/java/org/springframework/format/datetime/DateTimeFormatAnnotationFormatterFactory.java b/spring-context/src/main/java/org/springframework/format/datetime/DateTimeFormatAnnotationFormatterFactory.java
new file mode 100644
index 0000000000..6e6da41a4c
--- /dev/null
+++ b/spring-context/src/main/java/org/springframework/format/datetime/DateTimeFormatAnnotationFormatterFactory.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright 2002-2012 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;
+
+import java.util.Calendar;
+import java.util.Collections;
+import java.util.Date;
+import java.util.HashSet;
+import java.util.Set;
+
+import org.springframework.context.EmbeddedValueResolverAware;
+import org.springframework.format.AnnotationFormatterFactory;
+import org.springframework.format.Formatter;
+import org.springframework.format.Parser;
+import org.springframework.format.Printer;
+import org.springframework.format.annotation.DateTimeFormat;
+import org.springframework.format.datetime.joda.JodaDateTimeFormatAnnotationFormatterFactory;
+import org.springframework.util.StringValueResolver;
+
+/**
+ * Formats fields annotated with the {@link DateTimeFormat} annotation.
+ *
+ * @author Phillip Webb
+ * @see JodaDateTimeFormatAnnotationFormatterFactory
+ * @since 3.2
+ */
+public class DateTimeFormatAnnotationFormatterFactory implements
+ AnnotationFormatterFactory, EmbeddedValueResolverAware {
+
+
+ private static final Set> FIELD_TYPES;
+ static {
+ Set> fieldTypes = new HashSet>();
+ fieldTypes.add(Date.class);
+ fieldTypes.add(Calendar.class);
+ fieldTypes.add(Long.class);
+ FIELD_TYPES = Collections.unmodifiableSet(fieldTypes);
+ }
+
+
+ private StringValueResolver embeddedValueResolver;
+
+
+ public void setEmbeddedValueResolver(StringValueResolver resolver) {
+ this.embeddedValueResolver = resolver;
+ }
+
+ public Set> getFieldTypes() {
+ return FIELD_TYPES;
+ }
+
+ public Printer> getPrinter(DateTimeFormat annotation, Class> fieldType) {
+ return getFormatter(annotation, fieldType);
+ }
+
+ public Parser> getParser(DateTimeFormat annotation, Class> fieldType) {
+ return getFormatter(annotation, fieldType);
+ }
+
+ protected Formatter getFormatter(DateTimeFormat annotation, Class> fieldType) {
+ DateFormatter formatter = new DateFormatter();
+ formatter.setStylePattern(resolveEmbeddedValue(annotation.style()));
+ formatter.setIso(annotation.iso());
+ formatter.setPattern(resolveEmbeddedValue(annotation.pattern()));
+ return formatter;
+ }
+
+ protected String resolveEmbeddedValue(String value) {
+ return (this.embeddedValueResolver != null ? this.embeddedValueResolver.resolveStringValue(value) : value);
+ }
+}
diff --git a/spring-context/src/main/java/org/springframework/format/datetime/joda/DateTimeFormatterFactory.java b/spring-context/src/main/java/org/springframework/format/datetime/joda/DateTimeFormatterFactory.java
new file mode 100644
index 0000000000..89cd13a8bd
--- /dev/null
+++ b/spring-context/src/main/java/org/springframework/format/datetime/joda/DateTimeFormatterFactory.java
@@ -0,0 +1,167 @@
+/*
+ * Copyright 2002-2012 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.joda;
+
+import java.util.TimeZone;
+
+import org.joda.time.DateTimeZone;
+import org.joda.time.format.DateTimeFormat;
+import org.joda.time.format.DateTimeFormatter;
+import org.joda.time.format.ISODateTimeFormat;
+import org.springframework.beans.factory.FactoryBean;
+import org.springframework.format.annotation.DateTimeFormat.ISO;
+import org.springframework.util.StringUtils;
+
+/**
+ * {@link FactoryBean} that creates a Joda {@link DateTimeFormatter}. Formatters will be
+ * created using the defined {@link #setPattern(String) pattern}, {@link #setIso(ISO) ISO}
+ * or {@link #setStyle(String) style} (considered in that order).
+ *
+ * @author Phillip Webb
+ * @see #getDateTimeFormatter()
+ * @see #getDateTimeFormatter(DateTimeFormatter)
+ * @since 3.2
+ */
+public class DateTimeFormatterFactory implements FactoryBean {
+
+ private ISO iso;
+
+ private String style;
+
+ private String pattern;
+
+ private TimeZone timeZone;
+
+
+ /**
+ * Create a new {@link DateTimeFormatterFactory} instance.
+ */
+ public DateTimeFormatterFactory() {
+ }
+
+ /**
+ * Create a new {@link DateTimeFormatterFactory} instance.
+ * @param pattern the pattern to use to format date values
+ */
+ public DateTimeFormatterFactory(String pattern) {
+ this.pattern = pattern;
+ }
+
+
+ public boolean isSingleton() {
+ return true;
+ }
+
+ public Class> getObjectType() {
+ return DateTimeFormatter.class;
+ }
+
+ public DateTimeFormatter getObject() throws Exception {
+ return getDateTimeFormatter();
+ }
+
+ /**
+ * Get a new DateTimeFormatter using this factory. If no specific
+ * {@link #setStyle(String) style} {@link #setIso(ISO) ISO} or
+ * {@link #setPattern(String) pattern} have been defined the
+ * {@link DateTimeFormat#mediumDateTime() medium date time format} will be used.
+ * @return a new date time formatter
+ * @see #getObject()
+ * @see #getDateTimeFormatter(DateTimeFormatter)
+ */
+ public DateTimeFormatter getDateTimeFormatter() {
+ return getDateTimeFormatter(DateTimeFormat.mediumDateTime());
+ }
+
+ /**
+ * Get a new DateTimeFormatter using this factory. If no specific
+ * {@link #setStyle(String) style} {@link #setIso(ISO) ISO} or
+ * {@link #setPattern(String) pattern} have been defined the specific
+ * {@code fallbackFormatter} will be used.
+ * @param fallbackFormatter the fall-back formatter to use when no specific factory
+ * properties have been set (can be {@code null}).
+ * @return a new date time formatter
+ */
+ public DateTimeFormatter getDateTimeFormatter(DateTimeFormatter fallbackFormatter) {
+ DateTimeFormatter dateTimeFormatter = createDateTimeFormatter();
+ if(dateTimeFormatter != null && this.timeZone != null) {
+ dateTimeFormatter.withZone(DateTimeZone.forTimeZone(this.timeZone));
+ }
+ return (dateTimeFormatter != null ? dateTimeFormatter : fallbackFormatter);
+ }
+
+ private DateTimeFormatter createDateTimeFormatter() {
+ if (StringUtils.hasLength(pattern)) {
+ return DateTimeFormat.forPattern(pattern);
+ }
+ if (iso != null && iso != ISO.NONE) {
+ if (iso == ISO.DATE) {
+ return ISODateTimeFormat.date();
+ }
+ if (iso == ISO.TIME) {
+ return ISODateTimeFormat.time();
+ }
+ return ISODateTimeFormat.dateTime();
+ }
+ if (StringUtils.hasLength(style)) {
+ return DateTimeFormat.forStyle(style);
+ }
+ return null;
+ }
+
+
+ /**
+ * Set the 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 character to use to format date values. The first character used for
+ * the date style, the second is for the time style. Supported characters are
+ *
+ * - 'S' = Small
+ * - 'M' = Medium
+ * - 'L' = Long
+ * - 'F' = Full
+ * - '-' = Omitted
+ *
+ * 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 for this date.
+ * @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;
+ }
+}
diff --git a/spring-context/src/main/java/org/springframework/format/datetime/joda/JodaDateTimeFormatAnnotationFormatterFactory.java b/spring-context/src/main/java/org/springframework/format/datetime/joda/JodaDateTimeFormatAnnotationFormatterFactory.java
index ad8bbb5beb..c9deee8971 100644
--- a/spring-context/src/main/java/org/springframework/format/datetime/joda/JodaDateTimeFormatAnnotationFormatterFactory.java
+++ b/spring-context/src/main/java/org/springframework/format/datetime/joda/JodaDateTimeFormatAnnotationFormatterFactory.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2010 the original author or authors.
+ * Copyright 2002-2012 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.
@@ -34,12 +34,10 @@ import org.springframework.format.AnnotationFormatterFactory;
import org.springframework.format.Parser;
import org.springframework.format.Printer;
import org.springframework.format.annotation.DateTimeFormat;
-import org.springframework.format.annotation.DateTimeFormat.ISO;
-import org.springframework.util.StringUtils;
import org.springframework.util.StringValueResolver;
/**
- * Formats fields annotated with the {@link DateTimeFormat} annotation.
+ * Formats fields annotated with the {@link DateTimeFormat} annotation using Joda time.
*
* @author Keith Donald
* @author Juergen Hoeller
@@ -49,20 +47,33 @@ import org.springframework.util.StringValueResolver;
public class JodaDateTimeFormatAnnotationFormatterFactory
implements AnnotationFormatterFactory, EmbeddedValueResolverAware {
- private final Set> fieldTypes;
+ private static final Set> FIELD_TYPES;
+ static {
+ // 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)
+ Set> fieldTypes = new HashSet>(7);
+ fieldTypes.add(ReadableInstant.class);
+ fieldTypes.add(LocalDate.class);
+ fieldTypes.add(LocalTime.class);
+ fieldTypes.add(LocalDateTime.class);
+ fieldTypes.add(Date.class);
+ fieldTypes.add(Calendar.class);
+ fieldTypes.add(Long.class);
+ FIELD_TYPES = Collections.unmodifiableSet(fieldTypes);
+ }
+
private StringValueResolver embeddedValueResolver;
-
- public JodaDateTimeFormatAnnotationFormatterFactory() {
- this.fieldTypes = createFieldTypes();
- }
public final Set> getFieldTypes() {
- return this.fieldTypes;
+ return FIELD_TYPES;
}
-
public void setEmbeddedValueResolver(StringValueResolver resolver) {
this.embeddedValueResolver = resolver;
}
@@ -71,79 +82,42 @@ public class JodaDateTimeFormatAnnotationFormatterFactory
return (this.embeddedValueResolver != null ? this.embeddedValueResolver.resolveStringValue(value) : value);
}
-
public Printer> getPrinter(DateTimeFormat annotation, Class> fieldType) {
- DateTimeFormatter formatter = configureDateTimeFormatterFrom(annotation);
+ DateTimeFormatter formatter = getFormatter(annotation, fieldType);
+
if (ReadableInstant.class.isAssignableFrom(fieldType)) {
return new ReadableInstantPrinter(formatter);
}
- else if (ReadablePartial.class.isAssignableFrom(fieldType)) {
+
+ if (ReadablePartial.class.isAssignableFrom(fieldType)) {
return new ReadablePartialPrinter(formatter);
}
- else if (Calendar.class.isAssignableFrom(fieldType)) {
+
+ if (Calendar.class.isAssignableFrom(fieldType)) {
// assumes Calendar->ReadableInstant converter is registered
- return new ReadableInstantPrinter(formatter);
+ return new ReadableInstantPrinter(formatter);
}
- else {
- // assumes Date->Long converter is registered
- return new MillisecondInstantPrinter(formatter);
- }
+
+ // assumes Date->Long converter is registered
+ return new MillisecondInstantPrinter(formatter);
}
public Parser getParser(DateTimeFormat annotation, Class> fieldType) {
- return new DateTimeParser(configureDateTimeFormatterFrom(annotation));
+ return new DateTimeParser(getFormatter(annotation, fieldType));
}
- // 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)
+ /**
+ * Factory method used to create a {@link DateTimeFormatter}.
+ * @param annotation the format annotation for the field
+ * @param fieldType the type of field
+ * @return a {@link DateTimeFormatter} instance
+ * @since 3.2
*/
- 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);
+ protected DateTimeFormatter getFormatter(DateTimeFormat annotation, Class> fieldType) {
+ DateTimeFormatterFactory factory = new DateTimeFormatterFactory();
+ factory.setStyle(resolveEmbeddedValue(annotation.style()));
+ factory.setIso(annotation.iso());
+ factory.setPattern(resolveEmbeddedValue(annotation.pattern()));
+ return factory.getDateTimeFormatter();
}
-
- private DateTimeFormatter configureDateTimeFormatterFrom(DateTimeFormat annotation) {
- if (StringUtils.hasLength(annotation.pattern())) {
- return forPattern(resolveEmbeddedValue(annotation.pattern()));
- }
- else if (annotation.iso() != ISO.NONE) {
- return forIso(annotation.iso());
- }
- else {
- return forStyle(resolveEmbeddedValue(annotation.style()));
- }
- }
-
- private DateTimeFormatter forPattern(String pattern) {
- return org.joda.time.format.DateTimeFormat.forPattern(pattern);
- }
-
- private DateTimeFormatter forStyle(String style) {
- return org.joda.time.format.DateTimeFormat.forStyle(style);
- }
-
- private DateTimeFormatter forIso(ISO iso) {
- if (iso == ISO.DATE) {
- return org.joda.time.format.ISODateTimeFormat.date();
- }
- else if (iso == ISO.TIME) {
- return org.joda.time.format.ISODateTimeFormat.time();
- }
- else {
- return org.joda.time.format.ISODateTimeFormat.dateTime();
- }
- }
-
}
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 05fc3c03db..6943ad4241 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
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2009 the original author or authors.
+ * Copyright 2002-2012 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.
@@ -29,6 +29,7 @@ import org.joda.time.MutableDateTime;
import org.joda.time.ReadableInstant;
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 Joda Time support into Spring's field formatting system.
@@ -43,6 +44,7 @@ final class JodaTimeConverters {
* @param registry the converter registry
*/
public static void registerConverters(ConverterRegistry registry) {
+ DateFormatterRegistrar.addDateConverters(registry);
registry.addConverter(new DateTimeToLocalDateConverter());
registry.addConverter(new DateTimeToLocalTimeConverter());
registry.addConverter(new DateTimeToLocalDateTimeConverter());
@@ -52,14 +54,13 @@ final class JodaTimeConverters {
registry.addConverter(new DateTimeToDateConverter());
registry.addConverter(new DateTimeToCalendarConverter());
registry.addConverter(new DateTimeToLongConverter());
- registry.addConverter(new DateToLongConverter());
registry.addConverter(new CalendarToReadableInstantConverter());
}
/**
* Used when binding a parsed DateTime to a LocalDate field.
- * @see DateTimeParser
+ * @see DateTimeParser
**/
private static class DateTimeToLocalDateConverter implements Converter {
public LocalDate convert(DateTime source) {
@@ -67,9 +68,9 @@ final class JodaTimeConverters {
}
}
- /**
- * Used when binding a parsed DateTime to a LocalTime field.
- * @see DateTimeParser
+ /**
+ * Used when binding a parsed DateTime to a LocalTime field.
+ * @see DateTimeParser
*/
private static class DateTimeToLocalTimeConverter implements Converter {
public LocalTime convert(DateTime source) {
@@ -77,9 +78,9 @@ final class JodaTimeConverters {
}
}
- /**
- * Used when binding a parsed DateTime to a LocalDateTime field.
- * @see DateTimeParser
+ /**
+ * Used when binding a parsed DateTime to a LocalDateTime field.
+ * @see DateTimeParser
*/
private static class DateTimeToLocalDateTimeConverter implements Converter {
public LocalDateTime convert(DateTime source) {
@@ -87,9 +88,9 @@ final class JodaTimeConverters {
}
}
- /**
- * Used when binding a parsed DateTime to a DateMidnight field.
- * @see DateTimeParser
+ /**
+ * Used when binding a parsed DateTime to a DateMidnight field.
+ * @see DateTimeParser
*/
private static class DateTimeToDateMidnightConverter implements Converter {
public DateMidnight convert(DateTime source) {
@@ -97,9 +98,9 @@ final class JodaTimeConverters {
}
}
- /**
- * Used when binding a parsed DateTime to an Instant field.
- * @see DateTimeParser
+ /**
+ * Used when binding a parsed DateTime to an Instant field.
+ * @see DateTimeParser
*/
private static class DateTimeToInstantConverter implements Converter {
public Instant convert(DateTime source) {
@@ -107,19 +108,19 @@ final class JodaTimeConverters {
}
}
- /**
- * Used when binding a parsed DateTime to a MutableDateTime field.
- * @see DateTimeParser
+ /**
+ * 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
+
+ /**
+ * Used when binding a parsed DateTime to a java.util.Date field.
+ * @see DateTimeParser
*/
private static class DateTimeToDateConverter implements Converter {
public Date convert(DateTime source) {
@@ -127,9 +128,9 @@ final class JodaTimeConverters {
}
}
- /**
- * Used when binding a parsed DateTime to a java.util.Calendar field.
- * @see DateTimeParser
+ /**
+ * Used when binding a parsed DateTime to a java.util.Calendar field.
+ * @see DateTimeParser
*/
private static class DateTimeToCalendarConverter implements Converter {
public Calendar convert(DateTime source) {
@@ -137,9 +138,9 @@ final class JodaTimeConverters {
}
}
- /**
- * Used when binding a parsed DateTime to a java.lang.Long field.
- * @see DateTimeParser
+ /**
+ * Used when binding a parsed DateTime to a java.lang.Long field.
+ * @see DateTimeParser
*/
private static class DateTimeToLongConverter implements Converter {
public Long convert(DateTime source) {
@@ -147,22 +148,11 @@ final class JodaTimeConverters {
}
}
- /**
- * Used when printing a java.util.Date field with a MillisecondInstantPrinter.
- * @see MillisecondInstantPrinter
- * @see JodaDateTimeFormatAnnotationFormatterFactory
- */
- private static class DateToLongConverter implements Converter {
- public Long convert(Date source) {
- return source.getTime();
- }
- }
-
- /**
+ /**
* Used when printing a java.util.Calendar field with a ReadableInstantPrinter.
* @see MillisecondInstantPrinter
* @see JodaDateTimeFormatAnnotationFormatterFactory
- */
+ */
private static class CalendarToReadableInstantConverter implements Converter {
public ReadableInstant convert(Calendar source) {
return new DateTime(source);
diff --git a/spring-context/src/main/java/org/springframework/format/datetime/joda/JodaTimeFormatterRegistrar.java b/spring-context/src/main/java/org/springframework/format/datetime/joda/JodaTimeFormatterRegistrar.java
index b60a6b3a0a..19a81f44c2 100644
--- a/spring-context/src/main/java/org/springframework/format/datetime/joda/JodaTimeFormatterRegistrar.java
+++ b/spring-context/src/main/java/org/springframework/format/datetime/joda/JodaTimeFormatterRegistrar.java
@@ -17,6 +17,8 @@ package org.springframework.format.datetime.joda;
import java.util.Calendar;
import java.util.Date;
+import java.util.HashMap;
+import java.util.Map;
import org.joda.time.DateTime;
import org.joda.time.LocalDate;
@@ -25,40 +27,54 @@ import org.joda.time.LocalTime;
import org.joda.time.ReadableInstant;
import org.joda.time.format.DateTimeFormat;
import org.joda.time.format.DateTimeFormatter;
-import org.joda.time.format.ISODateTimeFormat;
import org.springframework.format.FormatterRegistrar;
import org.springframework.format.FormatterRegistry;
import org.springframework.format.Parser;
import org.springframework.format.Printer;
+import org.springframework.format.annotation.DateTimeFormat.ISO;
+import org.springframework.format.datetime.DateFormatterRegistrar;
/**
* Configures Joda Time's Formatting system for use with Spring.
*
* @author Keith Donald
* @author Juergen Hoeller
+ * @author Phillip Webb
* @since 3.1
* @see #setDateStyle
* @see #setTimeStyle
* @see #setDateTimeStyle
* @see #setUseIsoFormat
* @see FormatterRegistrar#registerFormatters
+ * @see DateFormatterRegistrar
*/
public class JodaTimeFormatterRegistrar implements FormatterRegistrar {
- private String dateStyle;
+ /**
+ * User defined formatters.
+ */
+ private Map formatters = new HashMap();
- private String timeStyle;
+ /**
+ * Factories used when specific formatters have not been specified.
+ */
+ private Map factories;
- private String dateTimeStyle;
- private boolean useIsoFormat;
+ public JodaTimeFormatterRegistrar() {
+ this.factories = new HashMap();
+ for (Type type : Type.values()) {
+ this.factories.put(type, new DateTimeFormatterFactory());
+ }
+ }
+
/**
* Set the default format style of Joda {@link LocalDate} objects.
* Default is {@link DateTimeFormat#shortDate()}.
*/
public void setDateStyle(String dateStyle) {
- this.dateStyle = dateStyle;
+ factories.get(Type.DATE).setStyle(dateStyle+"-");
}
/**
@@ -66,7 +82,7 @@ public class JodaTimeFormatterRegistrar implements FormatterRegistrar {
* Default is {@link DateTimeFormat#shortTime()}.
*/
public void setTimeStyle(String timeStyle) {
- this.timeStyle = timeStyle;
+ factories.get(Type.TIME).setStyle("-"+timeStyle);
}
/**
@@ -75,7 +91,7 @@ public class JodaTimeFormatterRegistrar implements FormatterRegistrar {
* Default is {@link DateTimeFormat#shortDateTime()}.
*/
public void setDateTimeStyle(String dateTimeStyle) {
- this.dateTimeStyle = dateTimeStyle;
+ factories.get(Type.DATE_TIME).setStyle(dateTimeStyle);
}
/**
@@ -84,64 +100,108 @@ public class JodaTimeFormatterRegistrar implements FormatterRegistrar {
* If set to true, the dateStyle, timeStyle, and dateTimeStyle properties are ignored.
*/
public void setUseIsoFormat(boolean useIsoFormat) {
- this.useIsoFormat = useIsoFormat;
+ factories.get(Type.DATE).setIso(useIsoFormat ? ISO.DATE : null);
+ factories.get(Type.TIME).setIso(useIsoFormat ? ISO.TIME : null);
+ factories.get(Type.DATE_TIME).setIso(useIsoFormat ? ISO.DATE_TIME : null);
+ }
+
+ /**
+ * Set the formatter that will be used for objects representing date values.
+ * This formatter will be used for the {@link LocalDate} type. When specified
+ * {@link #setDateStyle(String) dateStyle} and
+ * {@link #setUseIsoFormat(boolean) useIsoFormat} properties will be ignored.
+ * @param formatter the formatter to use
+ * @see #setTimeFormatter(DateTimeFormatter)
+ * @see #setDateTimeFormatter(DateTimeFormatter)
+ * @since 3.2
+ */
+ public void setDateFormatter(DateTimeFormatter formatter) {
+ this.formatters.put(Type.DATE, formatter);
+ }
+
+ /**
+ * Set the formatter that will be used for objects representing date values.
+ * This formatter will be used for the {@link LocalTime} type. When specified
+ * {@link #setTimeStyle(String) timeStyle} and
+ * {@link #setUseIsoFormat(boolean) useIsoFormat} properties will be ignored.
+ * @param formatter the formatter to use
+ * @see #setDateFormatter(DateTimeFormatter)
+ * @see #setDateTimeFormatter(DateTimeFormatter)
+ * @since 3.2
+ */
+ public void setTimeFormatter(DateTimeFormatter formatter) {
+ this.formatters.put(Type.TIME, formatter);
+ }
+
+ /**
+ * Set the formatter that will be used for objects representing date and time values.
+ * This formatter will be used for {@link LocalDateTime}, {@link ReadableInstant},
+ * {@link Date} and {@link Calendar} types. When specified
+ * {@link #setDateTimeStyle(String) dateTimeStyle} and
+ * {@link #setUseIsoFormat(boolean) useIsoFormat} properties will be ignored.
+ * @param formatter the formatter to use
+ * @see #setDateFormatter(DateTimeFormatter)
+ * @see #setTimeFormatter(DateTimeFormatter)
+ * @since 3.2
+ */
+ public void setDateTimeFormatter(DateTimeFormatter formatter) {
+ this.formatters.put(Type.DATE_TIME, formatter);
}
public void registerFormatters(FormatterRegistry registry) {
JodaTimeConverters.registerConverters(registry);
- DateTimeFormatter jodaDateFormatter = getJodaDateFormatter();
- registry.addFormatterForFieldType(LocalDate.class, new ReadablePartialPrinter(jodaDateFormatter),
- new DateTimeParser(jodaDateFormatter));
+ DateTimeFormatter dateFormatter = getFormatter(Type.DATE);
+ DateTimeFormatter timeFormatter = getFormatter(Type.TIME);
+ DateTimeFormatter dateTimeFormatter = getFormatter(Type.DATE_TIME);
- DateTimeFormatter jodaTimeFormatter = getJodaTimeFormatter();
- registry.addFormatterForFieldType(LocalTime.class, new ReadablePartialPrinter(jodaTimeFormatter),
- new DateTimeParser(jodaTimeFormatter));
+ addFormatterForFields(registry,
+ new ReadablePartialPrinter(dateFormatter),
+ new DateTimeParser(dateFormatter),
+ LocalDate.class);
- DateTimeFormatter jodaDateTimeFormatter = getJodaDateTimeFormatter();
- Parser dateTimeParser = new DateTimeParser(jodaDateTimeFormatter);
- registry.addFormatterForFieldType(LocalDateTime.class, new ReadablePartialPrinter(jodaDateTimeFormatter),
- dateTimeParser);
+ addFormatterForFields(registry,
+ new ReadablePartialPrinter(timeFormatter),
+ new DateTimeParser(timeFormatter),
+ LocalTime.class);
- Printer readableInstantPrinter = new ReadableInstantPrinter(jodaDateTimeFormatter);
- registry.addFormatterForFieldType(ReadableInstant.class, readableInstantPrinter, dateTimeParser);
+ addFormatterForFields(registry,
+ new ReadablePartialPrinter(dateTimeFormatter),
+ new DateTimeParser(dateTimeFormatter),
+ LocalDateTime.class);
- registry.addFormatterForFieldAnnotation(new JodaDateTimeFormatAnnotationFormatterFactory());
+ addFormatterForFields(registry,
+ new ReadableInstantPrinter(dateTimeFormatter),
+ new DateTimeParser(dateTimeFormatter),
+ ReadableInstant.class, Date.class, Calendar.class);
+
+ registry.addFormatterForFieldAnnotation(
+ new JodaDateTimeFormatAnnotationFormatterFactory());
}
- // internal helpers
-
- private DateTimeFormatter getJodaDateFormatter() {
- if (this.useIsoFormat) {
- return ISODateTimeFormat.date();
+ private DateTimeFormatter getFormatter(Type type) {
+ DateTimeFormatter formatter = formatters.get(type);
+ if(formatter != null) {
+ return formatter;
}
- if (this.dateStyle != null) {
- return DateTimeFormat.forStyle(this.dateStyle + "-");
- } else {
- return DateTimeFormat.shortDate();
+ DateTimeFormatter fallbackFormatter = getFallbackFormatter(type);
+ return factories.get(type).getDateTimeFormatter(fallbackFormatter );
+ }
+
+ private DateTimeFormatter getFallbackFormatter(Type type) {
+ switch (type) {
+ case DATE: return DateTimeFormat.shortDate();
+ case TIME: return DateTimeFormat.shortTime();
+ default: return DateTimeFormat.shortDateTime();
}
}
- private DateTimeFormatter getJodaTimeFormatter() {
- if (this.useIsoFormat) {
- return ISODateTimeFormat.time();
- }
- if (this.timeStyle != null) {
- return DateTimeFormat.forStyle("-" + this.timeStyle);
- } else {
- return DateTimeFormat.shortTime();
- }
- }
-
- private DateTimeFormatter getJodaDateTimeFormatter() {
- if (this.useIsoFormat) {
- return ISODateTimeFormat.dateTime();
- }
- if (this.dateTimeStyle != null) {
- return DateTimeFormat.forStyle(this.dateTimeStyle);
- } else {
- return DateTimeFormat.shortDateTime();
+ private void addFormatterForFields(FormatterRegistry registry, Printer> printer,
+ Parser> parser, Class>... fieldTypes) {
+ for (Class> fieldType : fieldTypes) {
+ registry.addFormatterForFieldType(fieldType, printer, parser);
}
}
+ private static enum Type {DATE, TIME, DATE_TIME}
}
diff --git a/spring-context/src/main/java/org/springframework/format/support/DefaultFormattingConversionService.java b/spring-context/src/main/java/org/springframework/format/support/DefaultFormattingConversionService.java
index 47a410b7fb..5ed55dc0eb 100644
--- a/spring-context/src/main/java/org/springframework/format/support/DefaultFormattingConversionService.java
+++ b/spring-context/src/main/java/org/springframework/format/support/DefaultFormattingConversionService.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2011 the original author or authors.
+ * Copyright 2002-2012 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.
@@ -16,18 +16,9 @@
package org.springframework.format.support;
-import java.util.Calendar;
-import java.util.Collections;
-import java.util.Date;
-import java.util.HashSet;
-import java.util.Set;
-
import org.springframework.core.convert.support.DefaultConversionService;
-import org.springframework.format.AnnotationFormatterFactory;
import org.springframework.format.FormatterRegistry;
-import org.springframework.format.Parser;
-import org.springframework.format.Printer;
-import org.springframework.format.annotation.DateTimeFormat;
+import org.springframework.format.datetime.DateFormatterRegistrar;
import org.springframework.format.datetime.joda.JodaTimeFormatterRegistrar;
import org.springframework.format.number.NumberFormatAnnotationFormatterFactory;
import org.springframework.util.ClassUtils;
@@ -96,39 +87,9 @@ public class DefaultFormattingConversionService extends FormattingConversionServ
formatterRegistry.addFormatterForFieldAnnotation(new NumberFormatAnnotationFormatterFactory());
if (jodaTimePresent) {
new JodaTimeFormatterRegistrar().registerFormatters(formatterRegistry);
- } else {
- formatterRegistry.addFormatterForFieldAnnotation(new NoJodaDateTimeFormatAnnotationFormatterFactory());
}
- }
-
-
- /**
- * Dummy AnnotationFormatterFactory that simply fails if @DateTimeFormat is being used
- * without the JodaTime library being present.
- */
- private static final class NoJodaDateTimeFormatAnnotationFormatterFactory
- implements AnnotationFormatterFactory {
-
- private final Set> fieldTypes;
-
- public NoJodaDateTimeFormatAnnotationFormatterFactory() {
- Set> rawFieldTypes = new HashSet>(4);
- rawFieldTypes.add(Date.class);
- rawFieldTypes.add(Calendar.class);
- rawFieldTypes.add(Long.class);
- this.fieldTypes = Collections.unmodifiableSet(rawFieldTypes);
- }
-
- public Set> getFieldTypes() {
- return this.fieldTypes;
- }
-
- public Printer> getPrinter(DateTimeFormat annotation, Class> fieldType) {
- throw new IllegalStateException("JodaTime library not available - @DateTimeFormat not supported");
- }
-
- public Parser> getParser(DateTimeFormat annotation, Class> fieldType) {
- throw new IllegalStateException("JodaTime library not available - @DateTimeFormat not supported");
+ else {
+ new DateFormatterRegistrar().registerFormatters(formatterRegistry);
}
}
diff --git a/spring-context/src/test/java/org/springframework/format/datetime/DateFormatterTests.java b/spring-context/src/test/java/org/springframework/format/datetime/DateFormatterTests.java
index 668f420aab..3df68cdd88 100644
--- a/spring-context/src/test/java/org/springframework/format/datetime/DateFormatterTests.java
+++ b/spring-context/src/test/java/org/springframework/format/datetime/DateFormatterTests.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2009 the original author or authors.
+ * Copyright 2002-2012 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.
@@ -16,39 +16,217 @@
package org.springframework.format.datetime;
+import static org.hamcrest.Matchers.equalTo;
+import static org.hamcrest.Matchers.is;
+import static org.junit.Assert.assertThat;
+
+import java.text.DateFormat;
import java.text.ParseException;
import java.util.Calendar;
+import java.util.Date;
import java.util.Locale;
+import java.util.TimeZone;
-import static org.junit.Assert.*;
+import org.joda.time.DateTimeZone;
+import org.joda.time.format.DateTimeFormat;
+import org.joda.time.format.DateTimeFormatter;
+import org.junit.Rule;
import org.junit.Test;
-import org.springframework.format.datetime.DateFormatter;
+import org.junit.rules.ExpectedException;
+import org.springframework.format.annotation.DateTimeFormat.ISO;
/**
+ * Tests for {@link DateFormatter}.
+ *
* @author Keith Donald
+ * @author Phillip Webb
*/
public class DateFormatterTests {
- private DateFormatter formatter = new DateFormatter("yyyy-MM-dd");
-
+ @Rule
+ public ExpectedException thown = ExpectedException.none();
+
+ private static final TimeZone UTC = TimeZone.getTimeZone("UTC");
+
@Test
- public void formatValue() {
- Calendar cal = Calendar.getInstance(Locale.US);
- cal.clear();
- cal.set(Calendar.YEAR, 2009);
- cal.set(Calendar.MONTH, Calendar.JUNE);
- cal.set(Calendar.DAY_OF_MONTH, 1);
- assertEquals("2009-06-01", formatter.print(cal.getTime(), Locale.US));
+ public void shouldPrintAndParseDefault() throws Exception {
+ DateFormatter formatter = new DateFormatter();
+ formatter.setTimeZone(UTC);
+ Date date = getDate(2009, Calendar.JUNE, 1);
+ assertThat(formatter.print(date, Locale.US), is("Jun 1, 2009"));
+ assertThat(formatter.parse("Jun 1, 2009", Locale.US), is(date));
}
-
+
@Test
- public void parseValue() throws ParseException {
+ public void shouldPrintAndParseFromPattern() throws ParseException {
+ DateFormatter formatter = new DateFormatter("yyyy-MM-dd");
+ formatter.setTimeZone(UTC);
+ Date date = getDate(2009, Calendar.JUNE, 1);
+ assertThat(formatter.print(date, Locale.US), is("2009-06-01"));
+ assertThat(formatter.parse("2009-06-01", Locale.US), is(date));
+ }
+
+ @Test
+ public void shouldPrintAndParseShort() throws Exception {
+ DateFormatter formatter = new DateFormatter();
+ formatter.setTimeZone(UTC);
+ formatter.setStyle(DateFormat.SHORT);
+ Date date = getDate(2009, Calendar.JUNE, 1);
+ assertThat(formatter.print(date, Locale.US), is("6/1/09"));
+ assertThat(formatter.parse("6/1/09", Locale.US), is(date));
+ }
+
+ @Test
+ public void shouldPrintAndParseMedium() throws Exception {
+ DateFormatter formatter = new DateFormatter();
+ formatter.setTimeZone(UTC);
+ formatter.setStyle(DateFormat.MEDIUM);
+ Date date = getDate(2009, Calendar.JUNE, 1);
+ assertThat(formatter.print(date, Locale.US), is("Jun 1, 2009"));
+ assertThat(formatter.parse("Jun 1, 2009", Locale.US), is(date));
+ }
+
+ @Test
+ public void shouldPrintAndParseLong() throws Exception {
+ DateFormatter formatter = new DateFormatter();
+ formatter.setTimeZone(UTC);
+ formatter.setStyle(DateFormat.LONG);
+ Date date = getDate(2009, Calendar.JUNE, 1);
+ assertThat(formatter.print(date, Locale.US), is("June 1, 2009"));
+ assertThat(formatter.parse("June 1, 2009", Locale.US), is(date));
+ }
+
+ @Test
+ public void shouldPrintAndParseFull() throws Exception {
+ DateFormatter formatter = new DateFormatter();
+ formatter.setTimeZone(UTC);
+ formatter.setStyle(DateFormat.FULL);
+ Date date = getDate(2009, Calendar.JUNE, 1);
+ assertThat(formatter.print(date, Locale.US), is("Monday, June 1, 2009"));
+ assertThat(formatter.parse("Monday, June 1, 2009", Locale.US), is(date));
+ }
+
+ @Test
+ public void shouldPrintAndParseISODate() throws Exception {
+ DateFormatter formatter = new DateFormatter();
+ formatter.setTimeZone(UTC);
+ formatter.setIso(ISO.DATE);
+ Date date = getDate(2009, Calendar.JUNE, 1, 14, 23, 5, 3);
+ assertThat(formatter.print(date, Locale.US), is("2009-06-01"));
+ assertThat(formatter.parse("2009-6-01", Locale.US),
+ is(getDate(2009, Calendar.JUNE, 1)));
+ }
+
+ @Test
+ public void shouldPrintAndParseISOTime() throws Exception {
+ DateFormatter formatter = new DateFormatter();
+ formatter.setTimeZone(UTC);
+ formatter.setIso(ISO.TIME);
+ Date date = getDate(2009, Calendar.JANUARY, 1, 14, 23, 5, 3);
+ assertThat(formatter.print(date, Locale.US), is("14:23:05.003+0000"));
+ assertThat(formatter.parse("14:23:05.003+0000", Locale.US),
+ is(getDate(1970, Calendar.JANUARY, 1, 14, 23, 5, 3)));
+ }
+
+ @Test
+ public void shouldParseIsoTimeWithZeros() throws Exception {
+ DateFormatter formatter = new DateFormatter();
+ formatter.setIso(ISO.TIME);
+ Date date = formatter.parse("12:00:00.000-00005", Locale.US);
+ System.out.println(date);
+ }
+
+ @Test
+ public void shouldPrintAndParseISODateTime() throws Exception {
+ DateFormatter formatter = new DateFormatter();
+ formatter.setTimeZone(UTC);
+ formatter.setIso(ISO.DATE_TIME);
+ Date date = getDate(2009, Calendar.JUNE, 1, 14, 23, 5, 3);
+ assertThat(formatter.print(date, Locale.US), is("2009-06-01T14:23:05.003+0000"));
+ assertThat(formatter.parse("2009-06-01T14:23:05.003+0000", Locale.US), is(date));
+ }
+
+ @Test
+ public void shouldSupportJodaStylePatterns() throws Exception {
+ String[] chars = { "S", "M", "L", "F", "-" };
+ for (String d : chars) {
+ for (String t : chars) {
+ String style = d + t;
+ if (!style.equals("--")) {
+ Date date = getDate(2009, Calendar.JUNE, 10, 14, 23, 0, 0);
+ if (t.equals("-")) {
+ date = getDate(2009, Calendar.JUNE, 10);
+ } else if (d.equals("-")) {
+ date = getDate(1970, Calendar.JANUARY, 1, 14, 23, 0, 0);
+ }
+ testJodaStylePatterns(style, Locale.US, date);
+ }
+ }
+ }
+ }
+
+ private void testJodaStylePatterns(String style, Locale locale, Date date)
+ throws Exception {
+ DateFormatter formatter = new DateFormatter();
+ formatter.setTimeZone(UTC);
+ formatter.setStylePattern(style);
+ DateTimeFormatter jodaFormatter = DateTimeFormat.forStyle(style).withLocale(
+ locale).withZone(DateTimeZone.UTC);
+ String jodaPrinted = jodaFormatter.print(date.getTime());
+ assertThat("Unable to print style pattern " + style,
+ formatter.print(date, locale), is(equalTo(jodaPrinted)));
+ assertThat("Unable to parse style pattern " + style,
+ formatter.parse(jodaPrinted, locale), is(equalTo(date)));
+ }
+
+ @Test
+ public void shouldThrowOnUnsupportStylePattern() throws Exception {
+ DateFormatter formatter = new DateFormatter();
+ formatter.setStylePattern("OO");
+ thown.expect(IllegalStateException.class);
+ thown.expectMessage("Unsupported style pattern 'OO'");
+ formatter.parse("2009", Locale.US);
+ }
+
+ @Test
+ public void shouldUseCorrectOrder() throws Exception {
+ DateFormatter formatter = new DateFormatter();
+ formatter.setTimeZone(UTC);
+ formatter.setStyle(DateFormat.SHORT);
+ formatter.setStylePattern("L-");
+ formatter.setIso(ISO.DATE_TIME);
+ formatter.setPattern("yyyy");
+ Date date = getDate(2009, Calendar.JUNE, 1, 14, 23, 5, 3);
+
+ assertThat("uses pattern",formatter.print(date, Locale.US), is("2009"));
+
+ formatter.setPattern("");
+ assertThat("uses ISO", formatter.print(date, Locale.US), is("2009-06-01T14:23:05.003+0000"));
+
+ formatter.setIso(ISO.NONE);
+ assertThat("uses style pattern", formatter.print(date, Locale.US), is("June 1, 2009"));
+
+ formatter.setStylePattern("");
+ assertThat("uses style", formatter.print(date, Locale.US), is("6/1/09"));
+ }
+
+ private Date getDate(int year, int month, int dayOfMonth) {
+ return getDate(year, month, dayOfMonth, 0, 0, 0, 0);
+ }
+
+ private Date getDate(int year, int month, int dayOfMonth, int hour, int minute,
+ int second, int millisecond) {
Calendar cal = Calendar.getInstance(Locale.US);
+ cal.setTimeZone(UTC);
cal.clear();
- cal.set(Calendar.YEAR, 2009);
- cal.set(Calendar.MONTH, Calendar.JUNE);
- cal.set(Calendar.DAY_OF_MONTH, 1);
- assertEquals(cal.getTime(), formatter.parse("2009-06-01", Locale.US));
+ cal.set(Calendar.YEAR, year);
+ cal.set(Calendar.MONTH, month);
+ cal.set(Calendar.DAY_OF_MONTH, dayOfMonth);
+ cal.set(Calendar.HOUR, hour);
+ cal.set(Calendar.MINUTE, minute);
+ cal.set(Calendar.SECOND, second);
+ cal.set(Calendar.MILLISECOND, millisecond);
+ return cal.getTime();
}
}
diff --git a/spring-context/src/test/java/org/springframework/format/datetime/DateFormattingTests.java b/spring-context/src/test/java/org/springframework/format/datetime/DateFormattingTests.java
new file mode 100644
index 0000000000..087e8e9521
--- /dev/null
+++ b/spring-context/src/test/java/org/springframework/format/datetime/DateFormattingTests.java
@@ -0,0 +1,300 @@
+/*
+ * Copyright 2002-2012 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;
+
+import static org.junit.Assert.assertEquals;
+
+import java.util.ArrayList;
+import java.util.Calendar;
+import java.util.Date;
+import java.util.List;
+import java.util.Locale;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Ignore;
+import org.junit.Test;
+import org.springframework.beans.MutablePropertyValues;
+import org.springframework.context.i18n.LocaleContextHolder;
+import org.springframework.core.convert.support.DefaultConversionService;
+import org.springframework.format.annotation.DateTimeFormat;
+import org.springframework.format.annotation.DateTimeFormat.ISO;
+import org.springframework.format.datetime.DateFormatterRegistrar;
+import org.springframework.format.support.FormattingConversionService;
+import org.springframework.validation.DataBinder;
+
+/**
+ * @author Phillip Webb
+ * @author Keith Donald
+ * @author Juergen Hoeller
+ */
+public class DateFormattingTests {
+
+ private FormattingConversionService conversionService = new FormattingConversionService();
+
+ private DataBinder binder;
+
+ @Before
+ public void setUp() {
+ DefaultConversionService.addDefaultConverters(conversionService);
+
+ DateFormatterRegistrar registrar = new DateFormatterRegistrar();
+ registrar.registerFormatters(conversionService);
+
+ SimpleDateBean bean = new SimpleDateBean();
+ bean.getChildren().add(new SimpleDateBean());
+ binder = new DataBinder(bean);
+ binder.setConversionService(conversionService);
+
+ LocaleContextHolder.setLocale(Locale.US);
+ }
+
+ @After
+ public void tearDown() {
+ LocaleContextHolder.setLocale(null);
+ }
+
+ @Test
+ public void testBindDate() {
+ MutablePropertyValues propertyValues = new MutablePropertyValues();
+ propertyValues.add("date", "10/31/09 12:00 PM");
+ binder.bind(propertyValues);
+ assertEquals(0, binder.getBindingResult().getErrorCount());
+ assertEquals("10/31/09 12:00 PM", binder.getBindingResult().getFieldValue("date"));
+ }
+
+ @Test
+ public void testBindDateArray() {
+ MutablePropertyValues propertyValues = new MutablePropertyValues();
+ propertyValues.add("date", new String[] {"10/31/09 12:00 PM"});
+ binder.bind(propertyValues);
+ assertEquals(0, binder.getBindingResult().getErrorCount());
+ }
+
+ @Test
+ public void testBindDateAnnotated() {
+ MutablePropertyValues propertyValues = new MutablePropertyValues();
+ propertyValues.add("dateAnnotated", "10/31/09");
+ binder.bind(propertyValues);
+ assertEquals(0, binder.getBindingResult().getErrorCount());
+ assertEquals("10/31/09", binder.getBindingResult().getFieldValue("dateAnnotated"));
+ }
+
+ @Test
+ public void testBindDateAnnotatedWithError() {
+ MutablePropertyValues propertyValues = new MutablePropertyValues();
+ propertyValues.add("dateAnnotated", "Oct X31, 2009");
+ binder.bind(propertyValues);
+ assertEquals(1, binder.getBindingResult().getFieldErrorCount("dateAnnotated"));
+ assertEquals("Oct X31, 2009", binder.getBindingResult().getFieldValue("dateAnnotated"));
+ }
+
+ @Test
+ @Ignore
+ public void testBindDateAnnotatedWithFallbackError() {
+ // TODO This currently passes because of the Date(String) constructor fallback is used
+ MutablePropertyValues propertyValues = new MutablePropertyValues();
+ propertyValues.add("dateAnnotated", "Oct 031, 2009");
+ binder.bind(propertyValues);
+ assertEquals(1, binder.getBindingResult().getFieldErrorCount("dateAnnotated"));
+ assertEquals("Oct 031, 2009", binder.getBindingResult().getFieldValue("dateAnnotated"));
+ }
+
+ @Test
+ public void testBindCalendar() {
+ MutablePropertyValues propertyValues = new MutablePropertyValues();
+ propertyValues.add("calendar", "10/31/09 12:00 PM");
+ binder.bind(propertyValues);
+ assertEquals(0, binder.getBindingResult().getErrorCount());
+ assertEquals("10/31/09 12:00 PM", binder.getBindingResult().getFieldValue("calendar"));
+ }
+
+ @Test
+ public void testBindCalendarAnnotated() {
+ MutablePropertyValues propertyValues = new MutablePropertyValues();
+ propertyValues.add("calendarAnnotated", "10/31/09");
+ binder.bind(propertyValues);
+ assertEquals(0, binder.getBindingResult().getErrorCount());
+ assertEquals("10/31/09", binder.getBindingResult().getFieldValue("calendarAnnotated"));
+ }
+
+ @Test
+ public void testBindLong() {
+ MutablePropertyValues propertyValues = new MutablePropertyValues();
+ propertyValues.add("millis", "1256961600");
+ binder.bind(propertyValues);
+ assertEquals(0, binder.getBindingResult().getErrorCount());
+ assertEquals("1256961600", binder.getBindingResult().getFieldValue("millis"));
+ }
+
+ @Test
+ public void testBindLongAnnotated() {
+ MutablePropertyValues propertyValues = new MutablePropertyValues();
+ propertyValues.add("millisAnnotated", "10/31/09");
+ binder.bind(propertyValues);
+ assertEquals(0, binder.getBindingResult().getErrorCount());
+ assertEquals("10/31/09", binder.getBindingResult().getFieldValue("millisAnnotated"));
+ }
+
+ @Test
+ public void testBindISODate() {
+ MutablePropertyValues propertyValues = new MutablePropertyValues();
+ propertyValues.add("isoDate", "2009-10-31");
+ binder.bind(propertyValues);
+ assertEquals(0, binder.getBindingResult().getErrorCount());
+ assertEquals("2009-10-31", binder.getBindingResult().getFieldValue("isoDate"));
+ }
+
+ @Test
+ public void testBindISOTime() {
+ MutablePropertyValues propertyValues = new MutablePropertyValues();
+ propertyValues.add("isoTime", "12:00:00.000-0500");
+ binder.bind(propertyValues);
+ assertEquals(0, binder.getBindingResult().getErrorCount());
+ assertEquals("17:00:00.000+0000", binder.getBindingResult().getFieldValue("isoTime"));
+ }
+
+ @Test
+ public void testBindISODateTime() {
+ MutablePropertyValues propertyValues = new MutablePropertyValues();
+ propertyValues.add("isoDateTime", "2009-10-31T12:00:00.000-0800");
+ binder.bind(propertyValues);
+ assertEquals(0, binder.getBindingResult().getErrorCount());
+ assertEquals("2009-10-31T20:00:00.000+0000", binder.getBindingResult().getFieldValue("isoDateTime"));
+ }
+
+ @Test
+ public void testBindNestedDateAnnotated() {
+ MutablePropertyValues propertyValues = new MutablePropertyValues();
+ propertyValues.add("children[0].dateAnnotated", "10/31/09");
+ binder.bind(propertyValues);
+ assertEquals(0, binder.getBindingResult().getErrorCount());
+ assertEquals("10/31/09", binder.getBindingResult().getFieldValue("children[0].dateAnnotated"));
+ }
+
+ @SuppressWarnings("unused")
+ private static class SimpleDateBean {
+
+ @DateTimeFormat
+ private Date date;
+
+ @DateTimeFormat(style="S-")
+ private Date dateAnnotated;
+
+ @DateTimeFormat
+ private Calendar calendar;
+
+ @DateTimeFormat(style="S-")
+ private Calendar calendarAnnotated;
+
+ private Long millis;
+
+ private Long millisAnnotated;
+
+ @DateTimeFormat(pattern="M/d/yy h:mm a")
+ private Date dateAnnotatedPattern;
+
+ @DateTimeFormat(iso=ISO.DATE)
+ private Date isoDate;
+
+ @DateTimeFormat(iso=ISO.TIME)
+ private Date isoTime;
+
+ @DateTimeFormat(iso=ISO.DATE_TIME)
+ private Date isoDateTime;
+
+ private final List children = new ArrayList();
+
+ public Date getDate() {
+ return date;
+ }
+
+ public void setDate(Date date) {
+ this.date = date;
+ }
+
+ public Date getDateAnnotated() {
+ return dateAnnotated;
+ }
+
+ public void setDateAnnotated(Date dateAnnotated) {
+ this.dateAnnotated = dateAnnotated;
+ }
+
+ public Calendar getCalendar() {
+ return calendar;
+ }
+
+ public void setCalendar(Calendar calendar) {
+ this.calendar = calendar;
+ }
+
+ public Calendar getCalendarAnnotated() {
+ return calendarAnnotated;
+ }
+
+ public void setCalendarAnnotated(Calendar calendarAnnotated) {
+ this.calendarAnnotated = calendarAnnotated;
+ }
+
+ public Long getMillis() {
+ return millis;
+ }
+
+ public void setMillis(Long millis) {
+ this.millis = millis;
+ }
+
+ @DateTimeFormat(style="S-")
+ public Long getMillisAnnotated() {
+ return millisAnnotated;
+ }
+
+ public void setMillisAnnotated(@DateTimeFormat(style="S-") Long millisAnnotated) {
+ this.millisAnnotated = millisAnnotated;
+ }
+
+ public Date getIsoDate() {
+ return isoDate;
+ }
+
+ public void setIsoDate(Date isoDate) {
+ this.isoDate = isoDate;
+ }
+
+ public Date getIsoTime() {
+ return isoTime;
+ }
+
+ public void setIsoTime(Date isoTime) {
+ this.isoTime = isoTime;
+ }
+
+ public Date getIsoDateTime() {
+ return isoDateTime;
+ }
+
+ public void setIsoDateTime(Date isoDateTime) {
+ this.isoDateTime = isoDateTime;
+ }
+
+ public List getChildren() {
+ return children;
+ }
+ }
+
+}
diff --git a/spring-context/src/test/java/org/springframework/format/datetime/joda/DateTimeFormatterFactoryTests.java b/spring-context/src/test/java/org/springframework/format/datetime/joda/DateTimeFormatterFactoryTests.java
new file mode 100644
index 0000000000..1a27b8d33b
--- /dev/null
+++ b/spring-context/src/test/java/org/springframework/format/datetime/joda/DateTimeFormatterFactoryTests.java
@@ -0,0 +1,104 @@
+/*
+ * Copyright 2002-2012 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.joda;
+
+import static org.hamcrest.Matchers.equalTo;
+import static org.hamcrest.Matchers.is;
+import static org.hamcrest.Matchers.nullValue;
+import static org.hamcrest.Matchers.sameInstance;
+import static org.junit.Assert.*;
+
+import java.util.Locale;
+import java.util.TimeZone;
+
+import org.joda.time.DateTime;
+import org.joda.time.format.DateTimeFormat;
+import org.joda.time.format.DateTimeFormatter;
+import org.junit.Test;
+import org.springframework.format.annotation.DateTimeFormat.ISO;
+
+/**
+ * Tests for {@link DateTimeFormatterFactory}.
+ *
+ * @author Phillip Webb
+ */
+public class DateTimeFormatterFactoryTests {
+
+ private DateTimeFormatterFactory factory = new DateTimeFormatterFactory();
+
+ private DateTime dateTime = new DateTime(2009, 10, 21, 12, 10, 00, 00);
+
+ @Test
+ public void shouldDefaultToMediumFormat() throws Exception {
+ assertThat(factory.getObject(), is(equalTo(DateTimeFormat.mediumDateTime())));
+ assertThat(factory.getDateTimeFormatter(), is(equalTo(DateTimeFormat.mediumDateTime())));
+ }
+
+ @Test
+ public void shouldCreateFromPattern() throws Exception {
+ factory = new DateTimeFormatterFactory("yyyyMMddHHmmss");
+ DateTimeFormatter formatter = factory.getObject();
+ assertThat(formatter.print(dateTime), is("20091021121000"));
+ }
+
+ @Test
+ public void shouldBeSingleton() throws Exception {
+ assertThat(factory.isSingleton(), is(true));
+ }
+
+ @Test
+ @SuppressWarnings("rawtypes")
+ public void shouldCreateDateTimeFormatter() throws Exception {
+ assertThat(factory.getObjectType(), is(equalTo((Class)DateTimeFormatter.class)));
+ }
+
+ @Test
+ public void shouldGetDateTimeFormatterNullFallback() throws Exception {
+ DateTimeFormatter formatter = factory.getDateTimeFormatter(null);
+ assertThat(formatter, is(nullValue()));
+ }
+
+ @Test
+ public void shouldGetDateTimeFormatterFallback() throws Exception {
+ DateTimeFormatter fallback = DateTimeFormat.forStyle("LL");
+ DateTimeFormatter formatter = factory.getDateTimeFormatter(fallback);
+ assertThat(formatter, is(sameInstance(fallback)));
+ }
+
+ @Test
+ public void shouldGetDateTimeFormatter() throws Exception {
+ factory.setStyle("SS");
+ assertThat(applyLocale(factory.getDateTimeFormatter()).print(dateTime), is("10/21/09 12:10 PM"));
+
+ factory.setIso(ISO.DATE);
+ assertThat(applyLocale(factory.getDateTimeFormatter()).print(dateTime), is("2009-10-21"));
+
+ factory.setPattern("yyyyMMddHHmmss");
+ assertThat(factory.getDateTimeFormatter().print(dateTime), is("20091021121000"));
+ }
+
+ @Test
+ public void shouldGetWithTimeZone() throws Exception {
+ factory.setPattern("yyyyMMddHHmmss Z");
+ factory.setTimeZone(TimeZone.getTimeZone("-0700"));
+ assertThat(factory.getDateTimeFormatter().print(dateTime), is("20091021121000 -0700"));
+ }
+
+ private DateTimeFormatter applyLocale(DateTimeFormatter dateTimeFormatter) {
+ return dateTimeFormatter.withLocale(Locale.US);
+ }
+}
diff --git a/spring-context/src/test/java/org/springframework/format/datetime/joda/JodaTimeFormattingTests.java b/spring-context/src/test/java/org/springframework/format/datetime/joda/JodaTimeFormattingTests.java
index ca7a926f9a..597a3820b5 100644
--- a/spring-context/src/test/java/org/springframework/format/datetime/joda/JodaTimeFormattingTests.java
+++ b/spring-context/src/test/java/org/springframework/format/datetime/joda/JodaTimeFormattingTests.java
@@ -49,15 +49,20 @@ import static org.junit.Assert.*;
*/
public class JodaTimeFormattingTests {
- private FormattingConversionService conversionService = new FormattingConversionService();
+ private FormattingConversionService conversionService;
private DataBinder binder;
@Before
public void setUp() {
+ JodaTimeFormatterRegistrar registrar = new JodaTimeFormatterRegistrar();
+ setUp(registrar);
+ }
+
+ private void setUp(JodaTimeFormatterRegistrar registrar) {
+ conversionService = new FormattingConversionService();
DefaultConversionService.addDefaultConverters(conversionService);
- JodaTimeFormatterRegistrar registrar = new JodaTimeFormatterRegistrar();
registrar.registerFormatters(conversionService);
JodaTimeBean bean = new JodaTimeBean();
@@ -84,7 +89,7 @@ public class JodaTimeFormattingTests {
System.out.println(org.joda.time.format.DateTimeFormat.patternForStyle("LL", LocaleContextHolder.getLocale()));
System.out.println(org.joda.time.format.DateTimeFormat.patternForStyle("FF", LocaleContextHolder.getLocale()));
}
-
+
@Test
public void testBindLocalDate() {
MutablePropertyValues propertyValues = new MutablePropertyValues();
@@ -94,6 +99,30 @@ public class JodaTimeFormattingTests {
assertEquals("10/31/09", binder.getBindingResult().getFieldValue("localDate"));
}
+ @Test
+ public void testBindLocalDateWithSpecificStyle() throws Exception {
+ JodaTimeFormatterRegistrar registrar = new JodaTimeFormatterRegistrar();
+ registrar.setDateStyle("L");
+ setUp(registrar);
+ MutablePropertyValues propertyValues = new MutablePropertyValues();
+ propertyValues.add("localDate", "October 31, 2009");
+ binder.bind(propertyValues);
+ assertEquals(0, binder.getBindingResult().getErrorCount());
+ assertEquals("October 31, 2009", binder.getBindingResult().getFieldValue("localDate"));
+ }
+
+ @Test
+ public void testBindLocalDateWithSpecifcFormatter() throws Exception {
+ JodaTimeFormatterRegistrar registrar = new JodaTimeFormatterRegistrar();
+ registrar.setDateFormatter(org.joda.time.format.DateTimeFormat.forPattern("yyyyMMdd"));
+ setUp(registrar);
+ MutablePropertyValues propertyValues = new MutablePropertyValues();
+ propertyValues.add("localDate", "20091031");
+ binder.bind(propertyValues);
+ assertEquals(0, binder.getBindingResult().getErrorCount());
+ assertEquals("20091031", binder.getBindingResult().getFieldValue("localDate"));
+ }
+
@Test
public void testBindLocalDateArray() {
MutablePropertyValues propertyValues = new MutablePropertyValues();
@@ -158,6 +187,30 @@ public class JodaTimeFormattingTests {
assertEquals("12:00 PM", binder.getBindingResult().getFieldValue("localTime"));
}
+ @Test
+ public void testBindLocalTimeWithSpecificStyle() throws Exception {
+ JodaTimeFormatterRegistrar registrar = new JodaTimeFormatterRegistrar();
+ registrar.setTimeStyle("M");
+ setUp(registrar);
+ MutablePropertyValues propertyValues = new MutablePropertyValues();
+ propertyValues.add("localTime", "12:00:00 PM");
+ binder.bind(propertyValues);
+ assertEquals(0, binder.getBindingResult().getErrorCount());
+ assertEquals("12:00:00 PM", binder.getBindingResult().getFieldValue("localTime"));
+ }
+
+ @Test
+ public void testBindLocalTimeWithSpecificFormatter() throws Exception {
+ JodaTimeFormatterRegistrar registrar = new JodaTimeFormatterRegistrar();
+ registrar.setTimeFormatter(org.joda.time.format.DateTimeFormat.forPattern("HHmmss"));
+ setUp(registrar);
+ MutablePropertyValues propertyValues = new MutablePropertyValues();
+ propertyValues.add("localTime", "130000");
+ binder.bind(propertyValues);
+ assertEquals(0, binder.getBindingResult().getErrorCount());
+ assertEquals("130000", binder.getBindingResult().getFieldValue("localTime"));
+ }
+
@Test
public void testBindLocalTimeAnnotated() {
MutablePropertyValues propertyValues = new MutablePropertyValues();
@@ -205,6 +258,42 @@ public class JodaTimeFormattingTests {
assertEquals("10/31/09 12:00 PM", binder.getBindingResult().getFieldValue("dateTime"));
}
+ @Test
+ public void testBindDateTimeWithSpecificStyle() throws Exception {
+ JodaTimeFormatterRegistrar registrar = new JodaTimeFormatterRegistrar();
+ registrar.setDateTimeStyle("MM");
+ setUp(registrar);
+ MutablePropertyValues propertyValues = new MutablePropertyValues();
+ propertyValues.add("localDateTime", "Oct 31, 2009 12:00:00 PM");
+ binder.bind(propertyValues);
+ assertEquals(0, binder.getBindingResult().getErrorCount());
+ assertEquals("Oct 31, 2009 12:00:00 PM", binder.getBindingResult().getFieldValue("localDateTime"));
+ }
+
+ @Test
+ public void testBindDateTimeISO() throws Exception {
+ JodaTimeFormatterRegistrar registrar = new JodaTimeFormatterRegistrar();
+ registrar.setUseIsoFormat(true);
+ setUp(registrar);
+ MutablePropertyValues propertyValues = new MutablePropertyValues();
+ propertyValues.add("dateTime", "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("dateTime"));
+ }
+
+ @Test
+ public void testBindDateTimeWithSpecificFormatter() throws Exception {
+ JodaTimeFormatterRegistrar registrar = new JodaTimeFormatterRegistrar();
+ registrar.setDateTimeFormatter(org.joda.time.format.DateTimeFormat.forPattern("yyyyMMddHHmmss"));
+ setUp(registrar);
+ MutablePropertyValues propertyValues = new MutablePropertyValues();
+ propertyValues.add("dateTime", "20091031130000");
+ binder.bind(propertyValues);
+ assertEquals(0, binder.getBindingResult().getErrorCount());
+ assertEquals("20091031130000", binder.getBindingResult().getFieldValue("dateTime"));
+ }
+
@Test
public void testBindDateTimeAnnotated() {
MutablePropertyValues propertyValues = new MutablePropertyValues();
@@ -339,7 +428,7 @@ public class JodaTimeFormattingTests {
assertEquals(0, binder.getBindingResult().getErrorCount());
assertEquals("10/31/09 12:00 PM", binder.getBindingResult().getFieldValue("instant"));
}
-
+
@Test
public void testBindInstantAnnotated() {
MutablePropertyValues propertyValues = new MutablePropertyValues();
@@ -357,7 +446,7 @@ public class JodaTimeFormattingTests {
assertEquals(0, binder.getBindingResult().getErrorCount());
assertEquals("10/31/09 12:00 PM", binder.getBindingResult().getFieldValue("mutableDateTime"));
}
-
+
@Test
public void testBindMutableDateTimeAnnotated() {
MutablePropertyValues propertyValues = new MutablePropertyValues();
@@ -483,7 +572,7 @@ public class JodaTimeFormattingTests {
public void setLocalDateTimeAnnotated(LocalDateTime localDateTimeAnnotated) {
this.localDateTimeAnnotated = localDateTimeAnnotated;
}
-
+
public LocalDateTime getLocalDateTimeAnnotatedLong() {
return localDateTimeAnnotatedLong;
}
@@ -596,7 +685,7 @@ public class JodaTimeFormattingTests {
public void setIsoDateTime(DateTime isoDateTime) {
this.isoDateTime = isoDateTime;
}
-
+
public Instant getInstant() {
return instant;
}