Skip to content

Commit 201fb5e

Browse files
committed
Make Joda DateTime serialization format configurable
We allow the serialization format of dates to be configured using spring.jackson.date-format. However, this property only applies to java.util.Date instances and has no effect on a Joda DateTime. This commit updates our auto-configuration for Jackson to allow the format string that is used to serialize a Joda DateTime to be configured. A new property, spring.jackson.joda-date-time-format has been introduced. When configured, it is used to configure the serialization format for a Joda DateTime. When it is not configured, we fall back to using spring.jackson.date-format. If this fails, either because the format string is incompatible (unlikely) or because the user's configured the fully-qualified name of a DateFormat class, a warning is logged encouraging the use of spring.jackson.joda-date-time-format. Fixes gh-2225
1 parent f11bcb9 commit 201fb5e

File tree

3 files changed

+86
-0
lines changed

3 files changed

+86
-0
lines changed

spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jackson/JacksonAutoConfiguration.java

+51
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,10 @@
2525

2626
import javax.annotation.PostConstruct;
2727

28+
import org.apache.commons.logging.Log;
29+
import org.apache.commons.logging.LogFactory;
30+
import org.joda.time.DateTime;
31+
import org.joda.time.format.DateTimeFormat;
2832
import org.springframework.beans.BeanUtils;
2933
import org.springframework.beans.factory.BeanFactoryUtils;
3034
import org.springframework.beans.factory.ListableBeanFactory;
@@ -47,6 +51,9 @@
4751
import com.fasterxml.jackson.databind.ObjectMapper;
4852
import com.fasterxml.jackson.databind.PropertyNamingStrategy;
4953
import com.fasterxml.jackson.databind.SerializationFeature;
54+
import com.fasterxml.jackson.databind.module.SimpleModule;
55+
import com.fasterxml.jackson.datatype.joda.ser.DateTimeSerializer;
56+
import com.fasterxml.jackson.datatype.joda.ser.JacksonJodaFormat;
5057

5158
/**
5259
* Auto configuration for Jackson. The following auto-configuration will get applied:
@@ -97,6 +104,50 @@ public ObjectMapper jacksonObjectMapper(Jackson2ObjectMapperBuilder builder) {
97104

98105
}
99106

107+
@Configuration
108+
@ConditionalOnClass({ Jackson2ObjectMapperBuilder.class, DateTime.class,
109+
DateTimeSerializer.class })
110+
static class JodaDateTimeJacksonConfiguration {
111+
112+
private final Log log = LogFactory.getLog(JodaDateTimeJacksonConfiguration.class);
113+
114+
@Autowired
115+
private JacksonProperties jacksonProperties;
116+
117+
@Bean
118+
public Module jodaDateTimeSerializationModule() {
119+
SimpleModule module = new SimpleModule();
120+
121+
JacksonJodaFormat jacksonJodaFormat = null;
122+
123+
if (this.jacksonProperties.getJodaDateTimeFormat() != null) {
124+
jacksonJodaFormat = new JacksonJodaFormat(DateTimeFormat.forPattern(
125+
this.jacksonProperties.getJodaDateTimeFormat()).withZoneUTC());
126+
}
127+
else if (this.jacksonProperties.getDateFormat() != null) {
128+
try {
129+
jacksonJodaFormat = new JacksonJodaFormat(DateTimeFormat.forPattern(
130+
this.jacksonProperties.getDateFormat()).withZoneUTC());
131+
}
132+
catch (IllegalArgumentException ex) {
133+
if (this.log.isWarnEnabled()) {
134+
this.log.warn("spring.jackson.date-format could not be used to "
135+
+ "configure formatting of Joda's DateTime. You may want "
136+
+ "to configure spring.jackson.joda-date-time-format as "
137+
+ "well.");
138+
}
139+
}
140+
}
141+
142+
if (jacksonJodaFormat != null) {
143+
module.addSerializer(DateTime.class, new DateTimeSerializer(
144+
jacksonJodaFormat));
145+
}
146+
147+
return module;
148+
}
149+
}
150+
100151
@Configuration
101152
@ConditionalOnClass({ ObjectMapper.class, Jackson2ObjectMapperBuilder.class })
102153
@EnableConfigurationProperties({ HttpMapperProperties.class, JacksonProperties.class })

spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jackson/JacksonProperties.java

+15
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,13 @@ public class JacksonProperties {
4343
*/
4444
private String dateFormat;
4545

46+
/**
47+
* Joda date time format string (yyyy-MM-dd HH:mm:ss). If not configured,
48+
* {@code date-format} will be used as a fallback if it is configured with a format
49+
* string.
50+
*/
51+
private String jodaDateTimeFormat;
52+
4653
/**
4754
* One of the constants on Jackson's PropertyNamingStrategy
4855
* (CAMEL_CASE_TO_LOWER_CASE_WITH_UNDERSCORES). Can also be a fully-qualified class
@@ -83,6 +90,14 @@ public void setDateFormat(String dateFormat) {
8390
this.dateFormat = dateFormat;
8491
}
8592

93+
public String getJodaDateTimeFormat() {
94+
return this.jodaDateTimeFormat;
95+
}
96+
97+
public void setJodaDateTimeFormat(String jodaDataTimeFormat) {
98+
this.jodaDateTimeFormat = jodaDataTimeFormat;
99+
}
100+
86101
public String getPropertyNamingStrategy() {
87102
return this.propertyNamingStrategy;
88103
}

spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/jackson/JacksonAutoConfigurationTests.java

+20
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
import java.util.Set;
2424

2525
import org.joda.time.DateTime;
26+
import org.joda.time.DateTimeZone;
2627
import org.joda.time.LocalDateTime;
2728
import org.junit.After;
2829
import org.junit.Before;
@@ -134,6 +135,23 @@ public void customDateFormat() throws Exception {
134135
"spring.jackson.date-format:yyyyMMddHHmmss");
135136
this.context.refresh();
136137
ObjectMapper mapper = this.context.getBean(ObjectMapper.class);
138+
DateTime dateTime = new DateTime(1988, 6, 25, 20, 30, DateTimeZone.UTC);
139+
assertEquals("\"19880625203000\"", mapper.writeValueAsString(dateTime));
140+
dateTime = new DateTime(1988, 6, 25, 20, 30);
141+
Date date = dateTime.toDate();
142+
assertEquals("\"19880625203000\"", mapper.writeValueAsString(date));
143+
}
144+
145+
@Test
146+
public void customJodaDateTimeFormat() throws Exception {
147+
this.context.register(JacksonAutoConfiguration.class);
148+
EnvironmentTestUtils.addEnvironment(this.context,
149+
"spring.jackson.date-format:yyyyMMddHHmmss",
150+
"spring.jackson.joda-date-time-format:yyyy-MM-dd HH:mm:ss");
151+
this.context.refresh();
152+
ObjectMapper mapper = this.context.getBean(ObjectMapper.class);
153+
DateTime dateTime = new DateTime(1988, 6, 25, 20, 30, DateTimeZone.UTC);
154+
assertEquals("\"1988-06-25 20:30:00\"", mapper.writeValueAsString(dateTime));
137155
Date date = new DateTime(1988, 6, 25, 20, 30).toDate();
138156
assertEquals("\"19880625203000\"", mapper.writeValueAsString(date));
139157
}
@@ -147,6 +165,8 @@ public void customDateFormatClass() throws Exception {
147165
"spring.jackson.date-format:org.springframework.boot.autoconfigure.jackson.JacksonAutoConfigurationTests.MyDateFormat");
148166
this.context.refresh();
149167
ObjectMapper mapper = this.context.getBean(ObjectMapper.class);
168+
DateTime dateTime = new DateTime(1988, 6, 25, 20, 30, DateTimeZone.UTC);
169+
assertEquals("\"1988-06-25T20:30:00.000Z\"", mapper.writeValueAsString(dateTime));
150170
Date date = new DateTime(1988, 6, 25, 20, 30).toDate();
151171
assertEquals("\"1988-06-25 20:30:00\"", mapper.writeValueAsString(date));
152172
}

0 commit comments

Comments
 (0)