跳到主要内容

时区问题

java.util.Date 配合 @JsonFormat 序列化时,时间会莫名其妙偏移(典型是少 8 小时)。

场景复现

@Setter
@Getter
class User {
private String username;

@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date date;
}

ObjectMapper objectMapper = new ObjectMapper();
User user = new User();
user.setUsername("zhangsan");
user.setDate(new Date()); // 系统时间 2021-09-25 10:48:30
System.out.println(objectMapper.writeValueAsString(user));

输出比系统时间少了 8 小时:

{
"username" : "zhangsan",
"date" : "2021-09-25 02:48:38"
}

换成 java.time.LocalDateTime 没这个问题,根因在时区。

原因

@JsonFormat 做日期格式化时,默认时区是 UTC,而不是 JVM 读到的操作系统时区。所以东八区的 Date 被按 UTC 格式化,正好少 8 小时。

解决

ObjectMapper 的默认时区设为 JVM 系统时区:

ObjectMapper objectMapper = new ObjectMapper();
objectMapper.setTimeZone(TimeZone.getDefault());

用 builder 配置时对应 builder.defaultTimeZone(TimeZone.getDefault()),见 ObjectMapper 配置

{
"username" : "zhangsan",
"date" : "2021-09-25 10:59:16"
}

为什么用 getDefault 而不是写死时区

写死成 TimeZone.getTimeZone("Asia/Shanghai") 也能解决,但不推荐。业务一旦跨地区部署(容器化部署尤其常见),给每个容器单独定制时区不现实。统一取 JVM 系统时区,让运维通过容器 / 系统时区一处控制,业务代码零改动。