0%

SpringMVC+FastJson 自定义日期转换器

对于有的时候要输出日期格式为yyyy-MM-dd,而有的时候要输出yyyy-MM-dd hh:mm:ss时怎么办?

第一种方案:纯注解式, 对日期类型的字段进行注解

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
@JSONField(format = "yyyy-MM-dd")
private Date updateDate;

@JSONField(format = "yyyy-MM-dd hh:mm:ss")
private Date createDate;

public Date getUpdateDate() {
return updateDate;
}

public void setUpdateDate(Date updateDate) {
this.updateDate = updateDate;
}

public void setCreateDate(Date createDate) {
this.createDate = createDate;
}

public Date getCreateDate() {
return createDate;
}

第二种方案:使用fastjson的WriteDateUseDateFormat配置(使得返回的日期类型默认为yyyy-MM-dd hh:mm:ss), 特殊类型使用字段@JSONField来进行控制

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
<!-- 默认的注解映射的支持,org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping -->
<mvc:annotation-driven content-negotiation-manager="contentNegotiationManager">
<mvc:message-converters register-defaults="true">
<!-- 将Jackson2HttpMessageConverter的默认格式化输出为true -->
<!-- 配置Fastjson支持 -->
<bean class="com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter">
<property name="supportedMediaTypes">
<list>
<value>text/html;charset=UTF-8</value>
<value>application/json</value>
</list>
</property>
<property name="features">
<list>
<!-- 输出key时是否使用双引号 -->
<value>QuoteFieldNames</value>
<!-- 是否输出值为null的字段 -->
<!-- <value>WriteMapNullValue</value> -->
<!-- 数值字段如果为null,输出为0,而非null -->
<value>WriteNullNumberAsZero</value>
<!-- List字段如果为null,输出为[],而非null -->
<value>WriteNullListAsEmpty</value>
<!-- 字符类型字段如果为null,输出为"",而非null -->
<value>WriteNullStringAsEmpty</value>
<!-- Boolean字段如果为null,输出为false,而非null -->
<value>WriteNullBooleanAsFalse</value>
<!-- null String不输出 -->
<value>WriteNullStringAsEmpty</value>
<!-- null String也要输出 -->
<!-- <value>WriteMapNullValue</value> -->

<!-- Date的日期转换器 -->
<value>WriteDateUseDateFormat</value>
</list>
</property>
</bean>
</mvc:message-converters>
</mvc:annotation-driven>

<!-- REST中根据URL后缀自动判定Content-Type及相应的View -->
<bean id="contentNegotiationManager" class="org.springframework.web.accept.ContentNegotiationManagerFactoryBean">
<property name="mediaTypes" >
<map>
<entry key="json" value="application/json"/>
</map>
</property>
<!-- 这里是否忽略掉accept header,默认就是false -->
<property name="ignoreAcceptHeader" value="true"/>
<property name="favorPathExtension" value="true"/>
</bean>
1
2
3
4
5
6
7
8
9
10
@JSONField(format = "yyyy-MM-dd")
private Date updateDate;

public Date getUpdateDate() {
return updateDate;
}

public void setUpdateDate(Date updateDate) {
this.updateDate = updateDate;
}

第三种方案:使用FastJson的消息转换器, 特殊类型使用字段@JSONField来进行控制

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.serializer.SerializeConfig;
import com.alibaba.fastjson.serializer.SimpleDateFormatSerializer;
import com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter;
import org.springframework.http.HttpOutputMessage;
import org.springframework.http.converter.HttpMessageNotWritableException;

import java.io.IOException;
import java.io.OutputStream;

/**
* 如果没有注入默认的日期格式,也没有配置<value>WriteDateUseDateFormat</value>, 也没有属性注解@JSONField(format="yyyy-MM-dd hh:mm:ss") 则会转换输出时间戳
* 如果只配置<value>WriteDateUseDateFormat</value>,则会转换输出yyyy-MM-dd hh:mm:ss
* 配置<value>WriteDateUseDateFormat</value>, 属性注解@JSONField(format="yyyy-MM-dd hh:mm:ss") 则会转换输出为属性注解的格式
* 如果注入了默认的日期格式,属性注解@JSONField(format="yyyy-MM-dd hh:mm:ss") 则会转换输出为属性注解的格式
* 如果注入了默认的日期格式,则会转换输出为默认的日期格式
* 如果三者都配置则会转换成属性注解的格式
* Created by PETER on 2016/2/5.
*/
public class CustomerFastJsonHttpMessageConverter extends FastJsonHttpMessageConverter {

public static SerializeConfig mapping = new SerializeConfig();

private String defaultDateFormat;

@Override
protected void writeInternal(Object obj, HttpOutputMessage outputMessage) throws IOException, HttpMessageNotWritableException {
OutputStream out = outputMessage.getBody();
String text = JSON.toJSONString(obj, mapping, super.getFeatures());
byte[] bytes = text.getBytes(getCharset());
out.write(bytes);
}

public void setDefaultDateFormat(String defaultDateFormat) {
mapping.put(java.util.Date.class, new SimpleDateFormatSerializer(defaultDateFormat));
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd
http://code.alibabatech.com/schema/dubbo http://code.alibabatech.com/schema/dubbo/dubbo.xsd
http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd">

<description>Spring MVC Configuration</description>

<!-- 加载配置属性文件 -->
<context:property-placeholder ignore-unresolvable="true" location="classpath:/xmutca.properties" />

<!-- 扫描dubbo注解需要在controller之前,否则会造成无法注入的问题 -->
<dubbo:annotation package="com.xmutca"></dubbo:annotation>

<!-- 使用Annotation自动注册Bean,只扫描@Controller -->
<context:component-scan base-package="com.xmutca" use-default-filters="false">
<!-- base-package 如果多个,用“,”分隔 -->
<context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>

<!-- 默认的注解映射的支持,org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping -->
<mvc:annotation-driven content-negotiation-manager="contentNegotiationManager">
<mvc:message-converters register-defaults="true">
<!-- 将Jackson2HttpMessageConverter的默认格式化输出为true -->
<!-- 配置Fastjson支持 -->
<bean class="com.ydyx.core.web.converter.CustomerFastJsonHttpMessageConverter">
<property name="supportedMediaTypes">
<list>
<value>text/html;charset=UTF-8</value>
<value>application/json</value>
</list>
</property>
<property name="features">
<list>
<!-- 输出key时是否使用双引号 -->
<value>QuoteFieldNames</value>
<!-- 是否输出值为null的字段 -->
<!-- <value>WriteMapNullValue</value> -->
<!-- 数值字段如果为null,输出为0,而非null -->
<value>WriteNullNumberAsZero</value>
<!-- List字段如果为null,输出为[],而非null -->
<value>WriteNullListAsEmpty</value>
<!-- 字符类型字段如果为null,输出为"",而非null -->
<value>WriteNullStringAsEmpty</value>
<!-- Boolean字段如果为null,输出为false,而非null -->
<value>WriteNullBooleanAsFalse</value>
<!-- null String不输出 -->
<value>WriteNullStringAsEmpty</value>
<!-- null String也要输出 -->
<!-- <value>WriteMapNullValue</value> -->
</list>
</property>
<property name="defaultDateFormat" value="yyyy-MM-dd"></property>
</bean>
</mvc:message-converters>
</mvc:annotation-driven>

<!-- REST中根据URL后缀自动判定Content-Type及相应的View -->
<bean id="contentNegotiationManager" class="org.springframework.web.accept.ContentNegotiationManagerFactoryBean">
<property name="mediaTypes" >
<map>
<entry key="json" value="application/json"/>
</map>
</property>
<!-- 这里是否忽略掉accept header,默认就是false -->
<property name="ignoreAcceptHeader" value="true"/>
<property name="favorPathExtension" value="true"/>
</bean>

<!-- 视图文件解析配置 -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="${web.view.prefix}"/>
<property name="suffix" value="${web.view.suffix}"/>
</bean>

<!-- 对静态资源文件的访问, 将无法mapping到Controller的path交给default servlet handler处理 -->
<mvc:default-servlet-handler/>

<!-- 定义无Controller的path<->view直接映射 -->
<mvc:view-controller path="/" view-name="redirect:${web.view.index}"/>

<!-- 基于注解式子的异常处理 -->
<bean id="exceptionHandlerExceptionResolver" class="org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver"></bean>
<!-- Shiro end -->

<!-- 上传文件拦截,设置最大上传文件大小 10M=10*1024*1024(B)=10485760 bytes -->
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<property name="maxUploadSize" value="${web.maxUploadSize}" />
</bean>
</beans>

第四种方案:使用SpringMVC的自定义属性编辑器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
@InitBinder
protected void initBinder(WebDataBinder binder) {
// String类型转换,将所有传递进来的String进行前后空格处理, null字符串处理
binder.registerCustomEditor(String.class, new PropertyEditorSupport() {
@Override
public void setAsText(String text) {
setValue(text == null ? null : text.trim());
}

@Override
public String getAsText() {
Object value = getValue();
return value != null ? value.toString() : "";
}
});

// Date 类型转换
binder.registerCustomEditor(Date.class, new PropertyEditorSupport() {
@Override
public void setAsText(String text) {
setValue(DateUtils.parseDate(text));
}

@Override
public String getAsText() {
Date date = (Date) getValue();
return DateUtils.formatDate(date, "yyyy-MM-dd");
}
});
}

DateUtils源代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;

import org.apache.commons.lang.time.DateFormatUtils;

/**
* 日期工具类, 继承org.apache.commons.lang.time.DateUtils类
*
*/
public class DateUtils extends org.apache.commons.lang3.time.DateUtils {

private static String[] parsePatterns = { "yyyy-MM-dd",
"yyyy-MM-dd HH:mm:ss", "yyyy-MM-dd HH:mm", "yyyy/MM/dd",
"yyyy/MM/dd HH:mm:ss", "yyyy/MM/dd HH:mm" ,"yyyyMMdd"};

/**
* 得到当前日期字符串 格式(yyyy-MM-dd)
*/
public static String getDate() {
return getDate("yyyy-MM-dd");
}

/**
* 得到当前日期字符串 格式(yyyy-MM-dd) pattern可以为:"yyyy-MM-dd" "HH:mm:ss" "E"
*/
public static String getDate(String pattern) {
return DateFormatUtils.format(new Date(), pattern);
}

/**
* 得到日期字符串 默认格式(yyyy-MM-dd) pattern可以为:"yyyy-MM-dd" "HH:mm:ss" "E"
*/
public static String formatDate(Date date, Object... pattern) {
String formatDate = null;
if (pattern != null && pattern.length > 0) {
formatDate = DateFormatUtils.format(date, pattern[0].toString());
} else {
formatDate = DateFormatUtils.format(date, "yyyy-MM-dd");
}
return formatDate;
}

/**
* 得到日期时间字符串,转换格式(yyyy-MM-dd HH:mm:ss)
*/
public static String formatDateTime(Date date) {
return formatDate(date, "yyyy-MM-dd HH:mm:ss");
}

/**
* 得到当前时间字符串 格式(HH:mm:ss)
*/
public static String getTime() {
return formatDate(new Date(), "HH:mm:ss");
}

/**
* 得到当前日期和时间字符串 格式(yyyy-MM-dd HH:mm:ss)
*/
public static String getDateTime() {
return formatDate(new Date(), "yyyy-MM-dd HH:mm:ss");
}

/**
* 得到当前年份字符串 格式(yyyy)
*/
public static String getYear() {
return formatDate(new Date(), "yyyy");
}

/**
* 得到当前月份字符串 格式(MM)
*/
public static String getMonth() {
return formatDate(new Date(), "MM");
}

/**
* 得到当天字符串 格式(dd)
*/
public static String getDay() {
return formatDate(new Date(), "dd");
}

/**
* 得到当前星期字符串 格式(E)星期几
*/
public static String getWeek() {
return formatDate(new Date(), "E");
}

/**
* 日期型字符串转化为日期 格式 { "yyyy-MM-dd", "yyyy-MM-dd HH:mm:ss", "yyyy-MM-dd HH:mm",
* "yyyy/MM/dd", "yyyy/MM/dd HH:mm:ss", "yyyy/MM/dd HH:mm", "yyyyMMdd" }
*/
public static Date parseDate(Object str) {
if (str == null) {
return null;
}
try {
return parseDate(str.toString(), parsePatterns);
} catch (ParseException e) {
return null;
}
}

/**
* 获取过去的天数
*
* @param date
* @return
*/
public static long pastDays(Date date) {
long t = new Date().getTime() - date.getTime();
return t / (24 * 60 * 60 * 1000);
}

/**
* 获取过去的小时
* @param date
* @return
*/
public static long pastHour(Date date) {
long t = new Date().getTime()-date.getTime();
return t/(60*60*1000);
}

/**
* 获取过去的分钟
* @param date
* @return
*/
public static long pastMinutes(Date date) {
long t = new Date().getTime()-date.getTime();
return t/(60*1000);
}

/**
* 转换为时间(天,时:分:秒.毫秒)
* @param timeMillis
* @return
*/
public static String formatDateTime(long timeMillis){
long day = timeMillis/(24*60*60*1000);
long hour = (timeMillis/(60*60*1000)-day*24);
long min = ((timeMillis/(60*1000))-day*24*60-hour*60);
long s = (timeMillis/1000-day*24*60*60-hour*60*60-min*60);
long sss = (timeMillis-day*24*60*60*1000-hour*60*60*1000-min*60*1000-s*1000);
return (day>0?day+",":"")+hour+":"+min+":"+s+"."+sss;
}

/**
* 获取某一天的开始时间(0点)
* @param date
* @return
*/
public static Date getDateStart(Date date) {
if (date == null) {
return null;
}
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
try {
date = sdf.parse(formatDate(date, "yyyy-MM-dd") + " 00:00:00");
} catch (ParseException e) {
e.printStackTrace();
}
return date;
}

/**
* 获取某一天的结束时间(23:59)
*
* @param date
* @return
*/
public static Date getDateEnd(Date date) {
if (date == null) {
return null;
}
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
try {
date = sdf.parse(formatDate(date, "yyyy-MM-dd") + " 23:59:59");
} catch (ParseException e) {
e.printStackTrace();
}
return date;
}

/**
* 比较两个日期时间的大小,反回1表示preDateStr > nextDateStr,0就相等,-1为小于
* @author: weihuang.peng
* @param preDateStr
* @param nextDateStr
* @return result
*/
public static int compareDate(Object preDateStr, Object nextDateStr) {
int result = 0;
Date preDate = parseDate(preDateStr);
Date nextDate = parseDate(nextDateStr);
try {
result = preDate.compareTo(nextDate);
} catch (Exception e) {
result = 0;
e.printStackTrace();
}
return result;
}

/**
* 获取某一天的前几天或者后几天,根据数字符号决定天数
* @author: weihuang.peng
* @param date
* @param days
* @return
*/
public static String getPastDayStr(Object dateObj, int days) {
Date date = parseDate(dateObj);
long time = date.getTime() + days * (long)(24 * 60 * 60 * 1000);
return formatDate(new Date(time));
}

/**
* preDateStr - nextDateStr 返回秒数
* @author: huiyang.yu
* @param preDateStr
* @param nextDateStr
* @return
*/
public static long getSubactDate(Object preDateStr, Object nextDateStr) {
Date preDate = parseDate(preDateStr);
Date nextDate = parseDate(nextDateStr);
long result = (preDate.getTime() - nextDate.getTime()) / 1000L;
return result;
}

/**
* 返回过去的天数: preDateStr - nextDateStr
* @author: weihuang.peng
* @param preDateStr
* @param nextDateStr
* @return
*/
public static long getDifferDate(Object preDateStr, Object nextDateStr) {
return getSubactDate(preDateStr, nextDateStr) / (60 * 60 * 24L);
}

/**
* 传入日期时间与当前系统日期时间的比较,
* 若日期相同,则根据时分秒来返回 ,
* 否则返回具体日期
* @author: huiyang.yu
* @param updateDate 传入日期
* @param updateTime 传入时间
* @return 日期或者 xx小时前||xx分钟前||xx秒前
*/
public static String getNewUpdateDateString(String updateDate, String updateTime) {
String result = updateDate;
long time = 0;
if (updateDate.equals(DateUtils.getDate())) {
time = DateUtils.getSubactDate(DateUtils.getDateTime(), updateDate
+ " " + updateTime);
if (time >= 3600) {
result = time / 3600 + "小时前";
} else if (time >= 60) {
result = time / 60 + "分钟前";
} else if (time >= 1) {
result = time + "秒前";
} else {
result = "刚刚";
}
} else if (result.length() >= 10) {
result = result.substring(5);
}
return result;
}
}