公司有个 ERP 对接的场景,我们需要将数据上报的接入方系统。在上报一段时间后发现订单数据的下单时间不正确,与正确的下单时间少了8小时…
说到这基本上就明白咋回事了,我当时特别奇怪。为了测试时间我还特意 DEBUG 了一下,结果显示 Domain 查询出来的日期是没问题的,但是经过 Jackson 序列化处理之后时间就少了八小时。当时是特别的诧异,现在我来复现下场景:
场景复现
创建一个普通的 User 类:
@Setter
@Getter
@ToString
class User {
private String username;
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date date;
}
注意看属性 date 字段,类型是 java.util.Date。并使用了 Jsckson 的 com.fasterxml.jackson.annotation.JsonFormat 注解指定日期格式化格式。
先看下当前操作系统的时间:
$ date +%Y-%m-%d\ %H:%M:%S
2021-09-25 10:48:30
如果正常的话那我系统输出的日期应该也是 10:48。但是实际上呢?下面来使用 ObjectMapper 做系列化:
public static void main(String[] args) throws JsonProcessingException {
ObjectMapper objectMapper = new ObjectMapper();
// 格式化输出
objectMapper.enable(SerializationFeature.INDENT_OUTPUT);
User user = new User();
user.setUsername("zhangsan");
user.setDate(new Date());
System.out.println(objectMapper.writeValueAsString(user));
}
结果输出如下:
{
"username" : "zhangsan",
"date" : "2021-09-25 02:48:38"
}
对比下时间你会发现整整差了8小时,但是如果将 java.util.Date 换成 java.time.LocalDateTime 就不会有这个问题,说到底还是时区的问题。
解决办法
为了解决该问题我看了下 com.fasterxml.jackson.annotation.JsonFormat 注解中对时区的说明,如下截图:
也就是说,Jackson 的 com.fasterxml.jackson.annotation.JsonFormat 注解在做日期格式化时默认选择的是 UTC 时区,而不是 JVM 读取的操作系统的时区。
知道了这点我们手动设置下时区不就好了吗?所以在创建 ObjectMapper 实例的时候我们做下时区的配置,将时区设置为 JVM 读取的操作系统的时区(推荐):
public static void main(String[] args) throws JsonProcessingException {
ObjectMapper objectMapper = new ObjectMapper();
// 格式化输出
objectMapper.enable(SerializationFeature.INDENT_OUTPUT);
// 设置时区
objectMapper.setTimeZone(TimeZone.getDefault());
User user = new User();
user.setUsername("zhangsan");
user.setDate(new Date());
System.out.println(objectMapper.writeValueAsString(user));
}
再次序列化就会发现时间正常了:
{
"username" : "zhangsan",
"date" : "2021-09-25 10:59:16"
}
疑问 |
---|
在设置时区时为什么选择 TimeZone.getDefault() ,直接指定具体时区不好吗?指定具体时区没什么问题,比如这个就可以选择中国时区:TimeZone.getTimeZone("Asia/Shanghai") 。但是不推荐,因为如果公司的业务在世界范围,在部署时都是容器化部署。你不可能为每个容器都定制一下时区吧?所以直接设置为 JVM 系统时区更适应业务。 |