Skip to content

Commit 4e97643

Browse files
committed
Merge pull request #161 from philwebb/SPR-7121
* SPR-7121: Support for custom global Joda DateTimeFormatters Move JRuby dependency below Joda Support DateTimeFormat annotation without Joda Corrected date pattern in JavaDocs Polish whitespace and formatting
2 parents 409b533 + 6660227 commit 4e97643

14 files changed

+1362
-247
lines changed

build.gradle

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -262,12 +262,12 @@ project('spring-context') {
262262
}
263263
compile("org.beanshell:bsh:2.0b4", optional)
264264
compile("org.codehaus.groovy:groovy-all:1.6.3", optional)
265-
compile("org.jruby:jruby:1.4.0", optional)
266265
compile("org.hibernate:hibernate-validator:4.2.0.Final") { dep ->
267266
optional dep
268267
exclude group: 'org.slf4j', module: 'slf4j-api'
269268
}
270269
compile("joda-time:joda-time:1.6", optional)
270+
compile("org.jruby:jruby:1.4.0", optional)
271271
compile("javax.cache:cache-api:0.5", optional)
272272
compile("net.sf.ehcache:ehcache-core:2.0.0", optional)
273273
compile("org.slf4j:slf4j-api:1.6.1", optional)

spring-context/src/main/java/org/springframework/format/annotation/DateTimeFormat.java

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@
2626
* Supports formatting by style pattern, ISO date time pattern, or custom format pattern string.
2727
* Can be applied to <code>java.util.Date</code>, <code>java.util.Calendar</code>, <code>java.long.Long</code>, or Joda Time fields.
2828
* <p>
29-
* For style-based formatting, set the {@link #style()} attribute to be the style pattern code.
29+
* For style-based formatting, set the {@link #style()} attribute to be the style pattern code.
3030
* The first character of the code is the date style, and the second character is the time style.
3131
* Specify a character of 'S' for short style, 'M' for medium, 'L' for long, and 'F' for full.
3232
* A date or time may be omitted by specifying the style character '-'.
@@ -39,7 +39,7 @@
3939
* When the pattern attribute is specified, it takes precedence over both the style and ISO attribute.
4040
* When the iso attribute is specified, if takes precedence over the style attribute.
4141
* When no annotation attributes are specified, the default format applied is style-based with a style code of 'SS' (short date, short time).
42-
*
42+
*
4343
* @author Keith Donald
4444
* @author Juergen Hoeller
4545
* @since 3.0
@@ -76,23 +76,23 @@
7676
* Common ISO date time format patterns.
7777
*/
7878
public enum ISO {
79-
80-
/**
79+
80+
/**
8181
* The most common ISO Date Format <code>yyyy-MM-dd</code> e.g. 2000-10-31.
8282
*/
8383
DATE,
8484

85-
/**
86-
* The most common ISO Time Format <code>hh:mm:ss.SSSZ</code> e.g. 01:30:00.000-05:00.
85+
/**
86+
* The most common ISO Time Format <code>HH:mm:ss.SSSZ</code> e.g. 01:30:00.000-05:00.
8787
*/
8888
TIME,
8989

90-
/**
91-
* The most common ISO DateTime Format <code>yyyy-MM-dd'T'hh:mm:ss.SSSZ</code> e.g. 2000-10-31 01:30:00.000-05:00.
90+
/**
91+
* The most common ISO DateTime Format <code>yyyy-MM-dd'T'HH:mm:ss.SSSZ</code> e.g. 2000-10-31 01:30:00.000-05:00.
9292
* The default if no annotation value is specified.
9393
*/
9494
DATE_TIME,
95-
95+
9696
/**
9797
* Indicates that no ISO-based format pattern should be applied.
9898
*/

spring-context/src/main/java/org/springframework/format/datetime/DateFormatter.java

Lines changed: 92 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2009 the original author or authors.
2+
* Copyright 2002-2012 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -19,27 +19,49 @@
1919
import java.text.DateFormat;
2020
import java.text.ParseException;
2121
import java.text.SimpleDateFormat;
22+
import java.util.Collections;
2223
import java.util.Date;
24+
import java.util.HashMap;
2325
import java.util.Locale;
26+
import java.util.Map;
2427
import java.util.TimeZone;
2528

2629
import org.springframework.format.Formatter;
30+
import org.springframework.format.annotation.DateTimeFormat;
31+
import org.springframework.format.annotation.DateTimeFormat.ISO;
32+
import org.springframework.util.Assert;
33+
import org.springframework.util.StringUtils;
2734

2835
/**
2936
* A formatter for {@link java.util.Date} types.
3037
* Allows the configuration of an explicit date pattern and locale.
3138
*
3239
* @author Keith Donald
3340
* @author Juergen Hoeller
41+
* @author Phillip Webb
3442
* @since 3.0
35-
* @see SimpleDateFormat
43+
* @see SimpleDateFormat
3644
*/
3745
public class DateFormatter implements Formatter<Date> {
3846

47+
private static final Map<ISO, String> ISO_PATTERNS;
48+
static {
49+
Map<ISO, String> formats = new HashMap<DateTimeFormat.ISO, String>();
50+
formats.put(ISO.DATE, "yyyy-MM-dd");
51+
formats.put(ISO.TIME, "HH:mm:ss.SSSZ");
52+
formats.put(ISO.DATE_TIME, "yyyy-MM-dd'T'HH:mm:ss.SSSZ");
53+
ISO_PATTERNS = Collections.unmodifiableMap(formats);
54+
}
55+
56+
3957
private String pattern;
4058

4159
private int style = DateFormat.DEFAULT;
4260

61+
private String stylePattern;
62+
63+
private ISO iso;
64+
4365
private TimeZone timeZone;
4466

4567
private boolean lenient = false;
@@ -80,6 +102,32 @@ public void setStyle(int style) {
80102
this.style = style;
81103
}
82104

105+
/**
106+
* Set the two character to use to format date values. The first character used for
107+
* the date style, the second is for the time style. Supported characters are
108+
* <ul>
109+
* <li>'S' = Small</li>
110+
* <li>'M' = Medium</li>
111+
* <li>'L' = Long</li>
112+
* <li>'F' = Full</li>
113+
* <li>'-' = Omitted</li>
114+
* <ul>
115+
* This method mimics the styles supported by Joda Time.
116+
* @param stylePattern two characters from the set {"S", "M", "L", "F", "-"}
117+
* @since 3.2
118+
*/
119+
public void setStylePattern(String stylePattern) {
120+
this.stylePattern = stylePattern;
121+
}
122+
123+
/**
124+
* Set the ISO format used for this date.
125+
* @param iso the {@link ISO} format
126+
* @since 3.2
127+
*/
128+
public void setIso(ISO iso) {
129+
this.iso = iso;
130+
}
83131
/**
84132
* Set the TimeZone to normalize the date values into, if any.
85133
*/
@@ -107,18 +155,53 @@ public Date parse(String text, Locale locale) throws ParseException {
107155

108156

109157
protected DateFormat getDateFormat(Locale locale) {
110-
DateFormat dateFormat;
111-
if (this.pattern != null) {
112-
dateFormat = new SimpleDateFormat(this.pattern, locale);
113-
}
114-
else {
115-
dateFormat = DateFormat.getDateInstance(this.style, locale);
116-
}
158+
DateFormat dateFormat = createDateFormat(locale);
117159
if (this.timeZone != null) {
118160
dateFormat.setTimeZone(this.timeZone);
119161
}
120162
dateFormat.setLenient(this.lenient);
121163
return dateFormat;
122164
}
123165

166+
private DateFormat createDateFormat(Locale locale) {
167+
if (StringUtils.hasLength(this.pattern)) {
168+
return new SimpleDateFormat(this.pattern, locale);
169+
}
170+
if (iso != null && iso != ISO.NONE) {
171+
String pattern = ISO_PATTERNS.get(iso);
172+
Assert.state(pattern != null, "Unsupported ISO format " + iso);
173+
SimpleDateFormat format = new SimpleDateFormat(pattern);
174+
format.setTimeZone(TimeZone.getTimeZone("UTC"));
175+
return format;
176+
}
177+
if(StringUtils.hasLength(stylePattern)) {
178+
int dateStyle = getStylePatternForChar(0);
179+
int timeStyle = getStylePatternForChar(1);
180+
if(dateStyle != -1 && timeStyle != -1) {
181+
return DateFormat.getDateTimeInstance(dateStyle, timeStyle, locale);
182+
}
183+
if(dateStyle != -1) {
184+
return DateFormat.getDateInstance(dateStyle, locale);
185+
}
186+
if(timeStyle != -1) {
187+
return DateFormat.getTimeInstance(timeStyle, locale);
188+
}
189+
throw new IllegalStateException("Unsupported style pattern '"+ stylePattern+ "'");
190+
191+
}
192+
return DateFormat.getDateInstance(this.style, locale);
193+
}
194+
195+
private int getStylePatternForChar(int index) {
196+
if(stylePattern != null && stylePattern.length() > index) {
197+
switch (stylePattern.charAt(index)) {
198+
case 'S': return DateFormat.SHORT;
199+
case 'M': return DateFormat.MEDIUM;
200+
case 'L': return DateFormat.LONG;
201+
case 'F': return DateFormat.FULL;
202+
case '-': return -1;
203+
}
204+
}
205+
throw new IllegalStateException("Unsupported style pattern '"+ stylePattern+ "'");
206+
}
124207
}
Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
/*
2+
* Copyright 2002-2012 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.format.datetime;
18+
19+
import java.util.Calendar;
20+
import java.util.Date;
21+
22+
import org.springframework.core.convert.converter.Converter;
23+
import org.springframework.core.convert.converter.ConverterRegistry;
24+
import org.springframework.format.FormatterRegistrar;
25+
import org.springframework.format.FormatterRegistry;
26+
import org.springframework.format.datetime.joda.JodaTimeFormatterRegistrar;
27+
import org.springframework.util.Assert;
28+
29+
/**
30+
* Configures Date formatting for use with Spring.
31+
* <p>
32+
* Designed for direct instantiation but also exposes the static
33+
* {@link #addDateConverters(ConverterRegistry)} utility method for ad hoc use against any
34+
* {@code ConverterRegistry} instance.
35+
*
36+
* @author Phillip Webb
37+
* @since 3.2
38+
* @see JodaTimeFormatterRegistrar
39+
* @see FormatterRegistrar#registerFormatters
40+
*/
41+
public class DateFormatterRegistrar implements FormatterRegistrar {
42+
43+
44+
private DateFormatter dateFormatter = new DateFormatter();
45+
46+
47+
public void registerFormatters(FormatterRegistry registry) {
48+
addDateConverters(registry);
49+
registry.addFormatter(dateFormatter);
50+
registry.addFormatterForFieldType(Calendar.class, dateFormatter);
51+
registry.addFormatterForFieldAnnotation(new DateTimeFormatAnnotationFormatterFactory());
52+
}
53+
54+
/**
55+
* Set the date formatter to register. If not specified default {@link DateFormatter}
56+
* will be used. This method can be used if additional formatter configuration is
57+
* required.
58+
* @param dateFormatter the date formatter
59+
*/
60+
public void setFormatter(DateFormatter dateFormatter) {
61+
Assert.notNull(dateFormatter,"DateFormatter must not be null");
62+
this.dateFormatter = dateFormatter;
63+
}
64+
65+
/**
66+
* Add date converters to the specified registry.
67+
* @param converterRegistry the registry of converters to add to
68+
*/
69+
public static void addDateConverters(ConverterRegistry converterRegistry) {
70+
converterRegistry.addConverter(new DateToLongConverter());
71+
converterRegistry.addConverter(new DateToCalendarConverter());
72+
converterRegistry.addConverter(new CalendarToDateConverter());
73+
converterRegistry.addConverter(new CalendarToLongConverter());
74+
converterRegistry.addConverter(new LongToDateConverter());
75+
converterRegistry.addConverter(new LongToCalendarConverter());
76+
}
77+
78+
79+
private static class DateToLongConverter implements Converter<Date, Long> {
80+
public Long convert(Date source) {
81+
return source.getTime();
82+
}
83+
}
84+
85+
86+
private static class DateToCalendarConverter implements Converter<Date, Calendar> {
87+
public Calendar convert(Date source) {
88+
Calendar calendar = Calendar.getInstance();
89+
calendar.setTime(source);
90+
return calendar;
91+
}
92+
}
93+
94+
95+
private static class CalendarToDateConverter implements Converter<Calendar, Date> {
96+
public Date convert(Calendar source) {
97+
return source.getTime();
98+
}
99+
}
100+
101+
102+
private static class CalendarToLongConverter implements Converter<Calendar, Long> {
103+
public Long convert(Calendar source) {
104+
return source.getTime().getTime();
105+
}
106+
}
107+
108+
109+
private static class LongToDateConverter implements Converter<Long, Date> {
110+
public Date convert(Long source) {
111+
return new Date(source);
112+
}
113+
}
114+
115+
116+
private static class LongToCalendarConverter implements Converter<Long, Calendar> {
117+
118+
private DateToCalendarConverter dateToCalendarConverter = new DateToCalendarConverter();
119+
120+
public Calendar convert(Long source) {
121+
return dateToCalendarConverter.convert(new Date(source));
122+
}
123+
}
124+
}

0 commit comments

Comments
 (0)