当前位置: 首页 > news >正文

SpringBoot项目里时间传参总乱套?手把手教你用@JsonFormat和@DateTimeFormat搞定前后端日期格式

SpringBoot时间传参乱码?@JsonFormat与@DateTimeFormat终极解决方案

1. 问题场景:前后端日期格式的"鸡同鸭讲"

上周团队新来的实习生小王遇到了一个典型问题:前端提交的订单创建时间2023-08-15 14:30:00,到后端变成了Wed Aug 15 14:30:00 CST 2023这种原始格式,而返回给前端时又成了1657895400000这样的时间戳。这种"格式变形记"在RESTful API开发中屡见不鲜,根本原因在于:

  • HTTP协议本身不定义日期格式标准,导致各系统自行其是
  • Java的Date对象与JSON字符串存在天然鸿沟,需要显式转换规则
  • 时区问题如同隐形炸弹,GMT、UTC、CST等时区混用会导致8小时的时间差
// 典型的问题实体类 public class Order { private Date createTime; // 裸奔的Date字段 // getters & setters }

当这个Order对象通过SpringBoot的@RestController返回时,Jackson会默认将Date序列化为时间戳;而前端提交JSON时,又期望接收yyyy-MM-dd格式的字符串。这种双向格式不匹配就是乱码的根源。

2. 注解双雄:各司其职的格式化方案

2.1 @JsonFormat:JSON序列化的守门人

这是Jackson库的核心注解,专门解决Java对象与JSON互转时的格式问题。它的典型配置:

@JsonFormat( pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8", shape = JsonFormat.Shape.STRING ) private Date createTime;

关键参数说明:

参数必要性示例值作用
pattern必选"yyyy-MM-dd"定义日期显示格式
timezone强烈建议"GMT+8"避免时区导致的8小时误差
shape可选Shape.STRING强制转为字符串而非时间戳

注意:在SpringBoot 2.x+版本中,默认已包含Jackson依赖。若项目异常,检查pom.xml是否包含:

<dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> </dependency>

2.2 @DateTimeFormat:HTTP参数的转换器

Spring框架提供的这个注解,专门处理URL参数和表单数据的日期转换:

@PostMapping("/orders") public ResponseEntity<?> createOrder( @RequestParam @DateTimeFormat(pattern = "yyyy-MM-dd") Date orderDate) { // 业务逻辑 }

常见使用场景对比:

场景适用注解示例
REST接口返回值@JsonFormat{"createTime":"2023-08-15"}
GET请求参数@DateTimeFormat/orders?date=2023-08-15
POST表单提交@DateTimeFormatdate=2023-08-15
JSON请求体@JsonFormat{"date":"2023-08-15"}

3. 实战组合拳:完整解决方案

3.1 实体类的最佳实践

@Data public class OrderDTO { @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") private Date createTime; // 其他字段... }

这种双注解组合能覆盖:

  • 前端 → 后端:表单提交、URL参数、JSON请求体
  • 后端 → 前端:JSON响应数据

3.2 控制层的典型配置

@RestController @RequestMapping("/api/orders") public class OrderController { // 处理URL参数 @GetMapping public List<Order> queryOrders( @RequestParam @DateTimeFormat(pattern = "yyyy-MM-dd") Date startDate, @RequestParam @DateTimeFormat(pattern = "yyyy-MM-dd") Date endDate) { return orderService.findByDateRange(startDate, endDate); } // 处理JSON请求体 @PostMapping public Order createOrder(@RequestBody OrderDTO dto) { return orderService.save(dto); } }

3.3 全局配置的增强方案

对于企业级应用,建议补充以下配置:

  1. Jackson全局配置(application.yml):
spring: jackson: date-format: yyyy-MM-dd HH:mm:ss time-zone: GMT+8
  1. 自定义日期转换器
@Configuration public class WebConfig implements WebMvcConfigurer { @Override public void addFormatters(FormatterRegistry registry) { DateTimeFormatterRegistrar registrar = new DateTimeFormatterRegistrar(); registrar.setUseIsoFormat(true); registrar.registerFormatters(registry); } }

4. 避坑指南:常见问题解析

4.1 时区问题的"八小时魔咒"

现象:数据库存的是2023-08-15 00:00:00,前端显示变成2023-08-14 16:00:00

解决方案

  1. 确保所有注解和配置统一时区(建议GMT+8
  2. 数据库连接字符串添加时区参数:
    jdbc:mysql://localhost:3306/db?serverTimezone=Asia/Shanghai

4.2 格式不匹配的解析异常

错误日志

Failed to convert value of type 'java.lang.String' to required type 'java.util.Date'

排查步骤

  1. 检查前端传递的格式是否与pattern定义一致
  2. 验证注解是否应用在正确的字段上
  3. 测试直接使用curl发送请求排除前端问题

4.3 日期比较的隐藏风险

// 错误的比较方式 if (new Date().equals(order.getCreateTime())) {...} // 正确做法 DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd"); if (formatter.format(LocalDate.now()).equals(formatter.format(order.getCreateTime()))) {...}

5. 高阶技巧:LocalDateTime的现代方案

对于新项目,推荐使用Java 8的日期API:

public class Order { @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") private LocalDateTime createTime; // 不需要@DateTimeFormat // 因为LocalDateTime默认支持ISO格式 }

优势对比:

特性java.util.DateLocalDateTime
线程安全
时区处理需要显式指定内置时区无关
API设计过时现代流畅式API
精度毫秒纳秒
// 日期运算示例 LocalDateTime now = LocalDateTime.now(); LocalDateTime tomorrow = now.plusDays(1); Duration duration = Duration.between(now, tomorrow);
http://www.cnnetsun.cn/news/2684599.html

相关文章:

  • 《HarmonyOS技术精讲》五:实战项目 ── 智能支架助手
  • 保姆级教程:在VMware里给openEuler虚拟机扩容磁盘,不重启搞定LVM分区
  • 告别模型降级与频繁断联:企业级 API 中转选型实测复盘及 Claude 避坑指南
  • C语言:文件操作(2)
  • LabVIEW 2021生成EXE后报表报错7?手把手教你添加NIReport.llb和LVClass文件
  • 监控画面总有雪花噪点?深入拆解海思/安霸芯片里的3D降噪技术到底是怎么工作的
  • LaMa图像修复模型训练避坑指南:从动态掩膜生成到损失函数调参
  • 从Cadence Tempus到Synopsys PT:手把手教你搞定两大神器下的check_timing检查
  • Flutter集成OpenAI API:构建流式AI对话应用的全栈实践
  • BK7231U SPI烧录避坑指南:从玄学Python脚本到稳定一键操作的进化之路
  • 超越基础教程:手把手教你用Niagara模块组合,打造更真实的游戏场景烟雾(含SubImageIndex随机技巧)
  • 避坑指南:动手仿真增量调制(∆M)过载与量化噪声(附MATLAB/Python代码)
  • 告别塑料玩具:聊聊工业级DLP光机在3D打印与扫描中如何‘扛’住产线环境
  • 基于GPT与Pytest的API自动化测试生成实践
  • Shell脚本进阶:用mapfile的-C回调函数,实现大文件读取的实时进度条
  • Arduino Uno + THB6128驱动板:从光耦限流计算到完整接线,搞定两相四线步进电机的保姆级避坑教程
  • 医疗AI智能体:从架构设计到临床落地的核心路径
  • 从晶体对称性到代码实现:高阶力常数插值中那些被你忽略的‘约束’到底怎么用?
  • 别再只聊NeRF了!3DGS实战:用Colmap+3D Gaussian Splatting快速重建你的房间(附完整代码)
  • 告别nRF Mesh APP:用ESP32自制BLE Mesh配网器,深入理解Provisioner底层事件与回调
  • 别再死记硬背了!用Input.GetAxis搞定Unity角色移动与旋转,附完整代码避坑
  • 倍福CX5130控制松下伺服:EtherCAT组网与轴参数调试避坑全记录
  • 别再手动调轮廓线了!分享一个我优化过的UE4高亮材质,直接拖进项目就能用
  • 别再乱编译OpenSSL了!CentOS 8/RHEL 8用户必须知道的系统库兼容性‘潜规则’
  • 别再傻傻分不清了!用FFmpeg实战演示RTMP直播推流与HLS点播切片(附完整命令)
  • 告别玄学!Python脚本全自动搞定BK7231U的SPI烧录(附完整代码)
  • 保姆级教程:在Mac M1/M2上用QEMU 8.2跑起Windows 10 ARM64(附驱动和避坑指南)
  • 别再手动拖拽了!用Resources.Load在Unity里动态换UI图片(附完整C#脚本)
  • 避开WinForm卡死!用MQTTnet做C#物联网应用时,异步和事件处理到底该怎么写?
  • 告别Log混乱!用CAPL的setLogFileName函数实现自动化测试日志的精准归档