一、遇到一个非常奇怪的bug
在本地开发环境中,从前端传入一个日期字段 reviewDate
(如 "2009-11-30"
),后端接收并保存到数据库时是正确的。
但部署到服务器后,同样的请求传入 "2009-11-30"
,最终在数据库中却变成了 "2009-11-29"
,日期自动减少了一天。
二、问题定位与根本原因
1. 使用类型:Date
+ @JsonFormat
原始代码如下:
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd", timezone = "GMT+8")
private Date reviewDate;
此注解表示 Jackson 会按照默认时区将前端传来的字符串解析为 Date
类型。
问题在于:Jackson 默认使用 JVM 的时区来解析日期字符串。
2. 服务器与本地环境的时区不同
- 本地环境运行在 GMT+8(中国标准时间)。
- 服务器运行在 UTC(或其他非 GMT+8 的时区)。
三、为什么 @JsonFormat(timezone = "GMT+8")
没有解决问题?
虽然这能确保 Jackson 使用 GMT+8 来解析日期,但由于以下原因仍可能失败:
- Java 中的
Date
是基于 UTC 时间戳的; - 如果数据库字段是
DATE
类型,并且数据库服务器运行在 UTC 时区,则仍然会把时间点解释为前一天; - 这意味着即使 Jackson 正确解析了日期,数据库层面的转换也会导致日期偏移。
四、最终解决方案
改用 LocalDate
替代 Date
@JsonFormat(pattern = "yyyy-MM-dd")
private LocalDate reviewDate;
效果:
LocalDate
不包含任何时间信息和时区概念;- Jackson 不会对它进行时区转换;
- 数据库字段为
DATE
类型即可正确保存; - 前后端传输一致,不再出现“少一天”问题。
五、建议扩展内容
如果你未来希望继续使用 Date
类型,推荐统一配置:
设置 Jackson 使用固定时区
@Configuration
public class JacksonConfig {
@Bean
public Jackson2ObjectMapperBuilder jackson2ObjectMapperBuilder() {
return new Jackson2ObjectMapperBuilder()
.timeZone(TimeZone.getTimeZone("GMT+8"));
}
}
配置数据库连接串时区(以 MySQL 为例):
spring.datasource.url=jdbc:mysql://localhost:3306/db?serverTimezone=Asia/Shanghai