Support DateTimeFormat annotation without Joda
Dependency on Joda Time when using the @DateTimeFormat annotation is now optional. If Joda Time is not present the JDK SimpleDateFormat will be used to parse and print date patterns. If Joda time is present it will always be used in preference to SimpleDateFormat. Issue: SPR-6508
This commit is contained in:
parent
ddddec8719
commit
a26059f94a
|
@ -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<Date> {
|
||||
|
||||
private static final Map<ISO, String> ISO_PATTERNS;
|
||||
static {
|
||||
Map<ISO, String> formats = new HashMap<DateTimeFormat.ISO, String>();
|
||||
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<Date> {
|
|||
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
|
||||
* <ul>
|
||||
* <li>'S' = Small</li>
|
||||
* <li>'M' = Medium</li>
|
||||
* <li>'L' = Long</li>
|
||||
* <li>'F' = Full</li>
|
||||
* <li>'-' = Omitted</li>
|
||||
* <ul>
|
||||
* 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<Date> {
|
|||
|
||||
|
||||
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<Date> {
|
|||
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+ "'");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,123 @@
|
|||
/*
|
||||
* 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.
|
||||
* <p>
|
||||
* 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.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<Date, Long> {
|
||||
public Long convert(Date source) {
|
||||
return source.getTime();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static class DateToCalendarConverter implements Converter<Date, Calendar> {
|
||||
public Calendar convert(Date source) {
|
||||
Calendar calendar = Calendar.getInstance();
|
||||
calendar.setTime(source);
|
||||
return calendar;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static class CalendarToDateConverter implements Converter<Calendar, Date> {
|
||||
public Date convert(Calendar source) {
|
||||
return source.getTime();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static class CalendarToLongConverter implements Converter<Calendar, Long> {
|
||||
public Long convert(Calendar source) {
|
||||
return source.getTime().getTime();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static class LongToDateConverter implements Converter<Long, Date> {
|
||||
public Date convert(Long source) {
|
||||
return new Date(source);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static class LongToCalendarConverter implements Converter<Long, Calendar> {
|
||||
|
||||
private DateToCalendarConverter dateToCalendarConverter = new DateToCalendarConverter();
|
||||
|
||||
public Calendar convert(Long source) {
|
||||
return dateToCalendarConverter.convert(new Date(source));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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<DateTimeFormat>, EmbeddedValueResolverAware {
|
||||
|
||||
|
||||
private static final Set<Class<?>> FIELD_TYPES;
|
||||
static {
|
||||
Set<Class<?>> fieldTypes = new HashSet<Class<?>>();
|
||||
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<Class<?>> 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<Date> 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);
|
||||
}
|
||||
}
|
|
@ -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.
|
||||
|
@ -39,7 +39,7 @@ 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
|
||||
|
|
|
@ -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,7 +54,6 @@ final class JodaTimeConverters {
|
|||
registry.addConverter(new DateTimeToDateConverter());
|
||||
registry.addConverter(new DateTimeToCalendarConverter());
|
||||
registry.addConverter(new DateTimeToLongConverter());
|
||||
registry.addConverter(new DateToLongConverter());
|
||||
registry.addConverter(new CalendarToReadableInstantConverter());
|
||||
}
|
||||
|
||||
|
@ -147,18 +148,7 @@ final class JodaTimeConverters {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Used when printing a java.util.Date field with a MillisecondInstantPrinter.
|
||||
* @see MillisecondInstantPrinter
|
||||
* @see JodaDateTimeFormatAnnotationFormatterFactory
|
||||
*/
|
||||
private static class DateToLongConverter implements Converter<Date, Long> {
|
||||
public Long convert(Date source) {
|
||||
return source.getTime();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
/**
|
||||
* Used when printing a java.util.Calendar field with a ReadableInstantPrinter.
|
||||
* @see MillisecondInstantPrinter
|
||||
* @see JodaDateTimeFormatAnnotationFormatterFactory
|
||||
|
|
|
@ -42,6 +42,7 @@ import org.springframework.format.Printer;
|
|||
* @see #setDateTimeStyle
|
||||
* @see #setUseIsoFormat
|
||||
* @see FormatterRegistrar#registerFormatters
|
||||
* @see DateFormatterRegistrar
|
||||
*/
|
||||
public class JodaTimeFormatterRegistrar implements FormatterRegistrar {
|
||||
|
||||
|
|
|
@ -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<DateTimeFormat> {
|
||||
|
||||
private final Set<Class<?>> fieldTypes;
|
||||
|
||||
public NoJodaDateTimeFormatAnnotationFormatterFactory() {
|
||||
Set<Class<?>> rawFieldTypes = new HashSet<Class<?>>(4);
|
||||
rawFieldTypes.add(Date.class);
|
||||
rawFieldTypes.add(Calendar.class);
|
||||
rawFieldTypes.add(Long.class);
|
||||
this.fieldTypes = Collections.unmodifiableSet(rawFieldTypes);
|
||||
}
|
||||
|
||||
public Set<Class<?>> 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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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<SimpleDateBean> children = new ArrayList<SimpleDateBean>();
|
||||
|
||||
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<SimpleDateBean> getChildren() {
|
||||
return children;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in New Issue