背景
今天在开发中发现通过spring的mybatis查出的时间比DB中的时间少一天,直接执行MySql的sql发现和DB中的时间一样,经过对比发现是时区问题。因为DB服务器是日本时区,取到的dto日期为中国时区,少了一个小时,但是因为date类型只取到天数,故少了一天。
解决
参考文章(来自[Architect剑]):https://www.cnblogs.com/yi1036943655/p/9854887.html
1、在启动类加上
@PostConstruct
void setDefaultTimezone() {
TimeZone.setDefault(TimeZone.getTimeZone("Asia/Shanghai"));
}
2、在application.properties加上
## json setting
spring.jackson.date-format=yyyy-MM-dd HH:mm:ss
spring.jackson.time-zone=Asia/Shanghai
3、在启动类 启动run方法里加上
public static void main(String[] args) {
TimeZone.setDefault(TimeZone.getTimeZone("Asia/Shanghai"));
SpringApplication.run(BaseMicroServiceApplication.class, args);
}
安装上面的方法试了之后果然得到解决,后台数据显示到前台页面没有问题了。
新问题
但是前台的date类型向后台传送时发生异常:
JSON parse error: Can not deserialize value of type java.util.Date from String "2018-03-07T16:18:35Z": not a valid representation (error: Failed to parse Date value '2018-03-07 16:18:35': Can not parse date "2018-03-07 16:18:35Z": while it seems to fit format 'yyyy-MM-dd HH:mm:ss'
对策
方案1,这里有个简单的方法,但是要每个对象都注解,果断选择下面一种。
https://blog.csdn.net/zhangminemail/article/details/83188522
方案2,好像是前后台的日期格式不一样,通过搜索找到如下解决方法:
原文地址(来自[黄雄杰]):https://blog.csdn.net/qq906627950/article/details/79503801
这篇文章分析了发生错误的原因,可谓是刨根问底。
但是安装作者的代码加上去之后,启动时出现空指针异常,原因是setTimeZone时没有calendar对象。
Caused by: java.lang.NullPointerException: null
at jp.co.irep.report.backend.app.config.MyDateFormat.setTimeZone(MyDateFormat.java:56)
at com.fasterxml.jackson.databind.cfg.BaseSettings._force(BaseSettings.java:356)
at com.fasterxml.jackson.databind.cfg.BaseSettings.withDateFormat(BaseSettings.java:226)
at com.fasterxml.jackson.databind.cfg.MapperConfigBase.with(MapperConfigBase.java:489)
at com.fasterxml.jackson.databind.ObjectMapper.setDateFormat(ObjectMapper.java:1855)
通过文章下面的评论又找到了另一篇对应方案:
https://blog.csdn.net/xtj332/article/details/84570181
这位作者([freewind])时把dateFormat的属性复制给了新的对象dateFormat1,然后序列化时将null都转成了“”。
这样楼主的问题时解决了,但是我这里又有新问题
异常:
Caused by: com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot construct instance of `java.util.ArrayList` (although at least one Creator exists): no String-argument constructor/factory method to deserialize from String value ('')
at [Source: (PushbackInputStream); line: 1, column: 946] (through reference chain: jp.co.irep.report.backend.dto.report008.Report008FormDto["rptBlckIndexList"])
at com.fasterxml.jackson.databind.exc.MismatchedInputException.from(MismatchedInputException.java:63)
原因是,我从前台向后台传了一个list 没数据时是null,这一转成“”完蛋了。
知道了问题所在就好修改了,我在[黄雄杰]的基础上稍微做了改善。
public MyDateFormat(DateFormat dateFormat) {
this.dateFormat = dateFormat;
this.calendar = dateFormat.getCalendar();
}
既然calendar属性是null,那我就在构造函数里给他个对象,😄
这样一来,启动的问题解决了。
但是跑起来还是同样的format转换问题,看来[黄雄杰]的修改并没有解决我的问题。
最后经过研究发现,前台传给后台的json中,日期格式是标准时间“yyyy-MM-dd'T'HH:mm:ss.SSS'Z'”
而后台已经被我们改成了非标准时间的“yyyy-MM-dd HH:mm:ss”,
如下,前台的JSON.stringify方法转json时默认使用标准时间:
http.interceptors.request.use(
config => {
if (config.method === 'post' ||
config.method === 'put' ||
config.method === 'delete') {
config.data = JSON.stringify(config.data)
}
showFullScreenLoading()
return config
},
error => {
Message({
showClose: true,
message: error,
type: 'error.data.error.message'
})
return Promise.reject(error.data.error.message)
}
)
既然如此,那我后台接收时按标准时间“yyyy-MM-dd'T'HH:mm:ss.SSS'Z'”来接收,然后转成spring的时间即可。最终在[黄雄杰]的代码基础上,修改了MyDateFormat的parse方法得以解决。
全部代码记录如下:
application.properties:
spring.jackson.date-format=yyyy-MM-dd HH:mm:ss
spring.jackson.time-zone=Japan
启动类:
@PostConstruct
void started() {
TimeZone.setDefault(TimeZone.getTimeZone("Japan"));
}
public static void main(String[] args) {
TimeZone.setDefault(TimeZone.getTimeZone("Japan"));
SpringApplication.run(MainApplication.class, args);
}
自己的DateFormat类:
import org.apache.commons.lang.StringUtils;
import java.text.DateFormat;
import java.text.FieldPosition;
import java.text.ParseException;
import java.text.ParsePosition;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.TimeZone;
public class MyDateFormat extends DateFormat {
private DateFormat dateFormat;
private SimpleDateFormat format1 = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
private SimpleDateFormat format2 = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'");
public MyDateFormat(DateFormat dateFormat) {
this.dateFormat = dateFormat;
this.calendar = dateFormat.getCalendar();
}
@Override
public StringBuffer format(Date date, StringBuffer toAppendTo, FieldPosition fieldPosition) {
return dateFormat.format(date, toAppendTo, fieldPosition);
}
@Override
public Date parse(String source, ParsePosition pos) {
Date date = null;
try {
date = format1.parse(source, pos);
} catch (Exception e) {
if (StringUtils.contains(source, "T")) {
format2.setTimeZone(TimeZone.getTimeZone("UTC"));
date = format1.parse(format1.format(format2.parse(source, pos)), pos);
} else {
date = dateFormat.parse(source, pos);
}
}
return date;
}
@Override
public Date parse(String source) throws ParseException {
Date date = null;
try {
date = format1.parse(source);
} catch (Exception e) {
if (StringUtils.contains(source, "T")) {
format2.setTimeZone(TimeZone.getTimeZone("UTC"));
date = format1.parse(format1.format(format2.parse(source)));
} else {
date = dateFormat.parse(source);
}
}
return date;
}
@Override
public Object clone() {
Object format = dateFormat.clone();
return new MyDateFormat((DateFormat) format);
}
}
配置DateFormat的config类:
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import java.text.DateFormat;
@Configuration
public class JacksonConfig {
@Autowired
private Jackson2ObjectMapperBuilder jackson2ObjectMapperBuilder;
@Bean
public MappingJackson2HttpMessageConverter MappingJsonpHttpMessageConverter() {
ObjectMapper mapper = jackson2ObjectMapperBuilder.build();
DateFormat dateFormat = mapper.getDateFormat();
mapper.setDateFormat(new MyDateFormat(dateFormat));
MappingJackson2HttpMessageConverter mappingJsonpHttpMessageConverter = new MappingJackson2HttpMessageConverter(mapper);
return mappingJsonpHttpMessageConverter;
}
}
到此问题得到完美解决,谢谢 博客园的[Architect剑],CSDN的[黄雄杰]以及[freewind]给我的启发。
如果这篇文章对你有帮助,可以分享给更多的人看到,谢谢。
最后打个广告,这是作者的微信公众号,欢迎关注。
网友评论