@DateTimeFormat 与 @JsonFormat:别再让日期格式坑惨你的接口!

做 Spring Boot 开发,处理日期格式是每天都要面对的事——前端传过来的日期字符串解析失败、后端返回的日期要么是时间戳要么是乱码的格式、跨时区返回的日期差 8 小时…这些问题几乎每个后端都踩过。
解决这类问题时,我经常随手把@DateTimeFormat和@JsonFormat堆在字段上,甚至混着用,以为这样就能搞定所有场景。结果上线后要么入参解析报错,要么返回格式不对,排查半天才发现:这两个注解的核心作用完全不一样,用错了等于白加。
今天把这两个注解的区别、用法、避坑点讲透,再也不用为日期格式头疼。
一句话分清,两个注解的核心区别
很多人搞混这两个注解,本质是没搞懂它们的“作用阶段”——一个管“进”,一个管“出”,完全不是一回事:
| 注解 | 核心作用 | 生效阶段 | 依赖框架 | 核心场景 |
|---|---|---|---|---|
| @DateTimeFormat | 格式化入参 | 前端→后端(接收参数) | Spring MVC | 接收前端传的日期字符串(如 2024-05-20),转成 Date/LocalDateTime |
| @JsonFormat | 格式化出参 | 后端→前端(返回数据) | Jackson | 把后端的 Date/LocalDateTime 转成指定格式的字符串返回给前端 |
简单说:
- 想让前端传的日期字符串能被后端正确接收,用
@DateTimeFormat; - 想让后端返回的日期不是时间戳/乱码,而是指定格式(如 yyyy-MM-dd HH:mm:ss),用
@JsonFormat; - 既想收参正常,又想返参格式对,就两个注解一起用(但要注意细节)。
最容易踩的 4 个坑
坑 1:用@JsonFormat 接收前端参数,完全无效
这是最高频的坑!很多人看到日期解析报错,就给入参字段加@JsonFormat,结果不管怎么配,前端传的“2024-05-20”还是解析成 Date 失败。
比如这段错误代码,加了@JsonFormat 但入参依然报错:
// 错误写法:想用@JsonFormat 接收前端入参
@PostMapping("/save")
public Result save(@RequestBody UserForm form) {
// 前端传{"createTime":"2024-05-20 12:00:00"},解析失败
return Result.success();
}
@Data
class UserForm {
// @JsonFormat 管出参,对入参无效!
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date createTime;
}
原因很简单:@JsonFormat是 Jackson 的注解,只负责把后端对象转 JSON 返回,根本管不了前端传参的解析。想解决入参问题,必须用@DateTimeFormat。
坑 2:用@DateTimeFormat 返回前端格式,没效果
和坑 1 反过来,有人想让返回的日期显示成“2024-05-20”,给字段加了@DateTimeFormat,结果返回的还是时间戳/默认格式:
// 错误写法:想用@DateTimeFormat 控制返回格式
@GetMapping("/get<User> getUser() {
User user = new User();
user.setCreateTime(new Date());
// 返回的 createTime 还是时间戳,@DateTimeFormat 完全没作用
return Result.success(user);
}
@Data
class User {
// @DateTimeFormat 管入参,对出参无效!
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date createTime;
}
@DateTimeFormat是 Spring MVC 的注解,只处理前端传过来的参数解析,不管返回格式,想控制出参必须用@JsonFormat。
坑 3:时区没配,返回的日期差 8 小时
这是第二个高频坑!用@JsonFormat时只配了pattern,没配时区,结果返回的日期比实际时间少 8 小时:
// 错误写法:缺省时区,返回日期差 8 小时
@Data
class User {
// 前端收到的时间会是 GMT 时间,比北京时间少 8 小时
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime createTime;
}
原因是 Jackson 默认用 GMT 时区,而我们用的是东八区(GMT+8),必须手动指定时区,否则必出问题。
坑 4:LocalDateTime/Date 混用注解,解析失败
Java8 的 LocalDateTime 和传统 Date 的注解配置不一样,混着配直接解析报错:
- Date 类型:可以直接用
@DateTimeFormat/@JsonFormat; - LocalDateTime 类型:
@DateTimeFormat需要指定 iso 属性,@JsonFormat要适配格式。
比如这段错误代码,LocalDateTime 用了 Date 的配置方式,直接报错:
@Data
class UserForm {
// 错误:LocalDateTime 用这种配置,前端传参解析失败
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime createTime;
}
正确用法:分场景配置(直接复制能用)
场景 1:只接收前端日期入参(用@DateTimeFormat)
不管是 GET 请求的参数,还是 POST 表单提交的参数,都用@DateTimeFormat:
// 1. GET 请求(路径参数/请求参数)
@GetMapping("/getByTime")
public Result getByTime(
// 前端传 ?createTime=2024-05-20 12:00:00,能正确解析
@RequestParam @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") Date createTime) {
return Result.success(createTime);
}
// 2. POST 表单请求
@PostMapping("/saveForm")
public Result saveForm(
// 表单传 createTime=2024-05-20,解析成 LocalDate
@RequestParam @DateTimeFormat(pattern = "yyyy-MM-dd") LocalDate createTime) {
return Result.success(createTime);
}
// 3. JSON 请求体(LocalDateTime 类型)
@PostMapping("/saveJson")
public Result saveJson(@RequestBody UserForm form) {
return Result.success(form.getCreateTime());
}
@Data
class UserForm {
// LocalDateTime 类型的入参,必须加 iso 属性
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss", iso = DateTimeFormat.ISO.DATE_TIME)
private LocalDateTime createTime;
}
场景 2:只返回前端日期出参(用@JsonFormat)
核心是必配时区timezone = "GMT+8",否则差 8 小时:
@GetMapping("/getUser<User> getUser() {
User user = new User();
user.setCreateTime(new Date());
user.setUpdateTime(LocalDateTime.now());
return Result.success(user);
}
@Data
class User {
// Date 类型出参:指定格式+时区
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
private Date createTime;
// LocalDateTime 类型出参:同样要指定时区
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
private LocalDateTime updateTime;
}
场景 3:既收参又返参(两个注解一起用)
这是最常见的场景,入参用@DateTimeFormat,出参用@JsonFormat,一起加在字段上:
@Data
class User {
// 入参解析:@DateTimeFormat 生效
// 出参格式化:@JsonFormat 生效
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
private Date createTime;
// LocalDateTime 类型的既收又返
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss", iso = DateTimeFormat.ISO.DATE_TIME)
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
private LocalDateTime updateTime;
}
场景 4:全局配置(不用每个字段加注解)
如果所有日期字段都要统一格式,不用挨个加注解,直接配全局规则,更省心:
# application.yml spring: # 1. 全局配置入参日期格式(对应@DateTimeFormat) mvc: format: date: yyyy-MM-dd date-time: yyyy-MM-dd HH:mm:ss # 2. 全局配置出参日期格式(对应@JsonFormat) jackson: date-format: yyyy-MM-dd HH:mm:ss time-zone: GMT+8 # 适配 LocalDateTime/LocalDate(Java8 时间类型) deserialization: adjust-dates-to-context-time-zone: true serialization: write-dates-as-timestamps: false
配置后,所有Date/LocalDateTime字段:
- 入参自动按配置的格式解析,不用加
@DateTimeFormat; - 出参自动按配置的格式返回,不用加
@JsonFormat; - 时区统一为东八区,不会差 8 小时。
线上真实踩坑案例:日期格式错导致订单时间混乱
某电商项目,用户提交订单时,前端传“2024-05-20 18:00:00”,后端用@JsonFormat接收,导致解析失败,订单创建时间默认成了当前时间,后续对账时发现大量订单时间错乱。
修复过程:
- 把入参字段的
@JsonFormat换成@DateTimeFormat; - 对
LocalDateTime类型补充了iso属性; - 全局配置了日期格式和时区,避免后续字段漏加注解;
- 上线后,订单时间解析 100%正确,对账问题彻底解决。
写在最后
@DateTimeFormat和@JsonFormat的坑,本质不是注解难用,而是没搞懂它们的“分工”——一个管进,一个管出。
记住 3 个核心点,再也不会踩坑:
- 收参用
@DateTimeFormat,返参用@JsonFormat,混着用没用; - 用
@JsonFormat必配timezone = "GMT+8",否则差 8 小时; LocalDateTime类型的@DateTimeFormat要加iso属性,别和 Date 混配。
如果项目里日期格式要求统一,优先用全局配置,不用每个字段加注解,既省心又不容易漏配。其实处理日期格式的问题,核心就是“对齐阶段、对齐时区、对齐类型”,把这三点做好,所有日期格式问题都能迎刃而解。
文章来源公众号:Java 患者
以上关于@DateTimeFormat 与 @JsonFormat:别再让日期格式坑惨你的接口!的文章就介绍到这了,更多相关内容请搜索码云笔记以前的文章或继续浏览下面的相关文章,希望大家以后多多支持码云笔记。
如若内容造成侵权/违法违规/事实不符,请将相关资料发送至 admin@mybj123.com 进行投诉反馈,一经查实,立即处理!
重要:如软件存在付费、会员、充值等,均属软件开发者或所属公司行为,与本站无关,网友需自行判断
码云笔记 » @DateTimeFormat 与 @JsonFormat:别再让日期格式坑惨你的接口!
微信
支付宝