问题背景
在Spring Web开发中,前后端交互经常会遇到日期时间格式的问题。默认情况下:
- 后端返回给前端的时间戳是一个长整型数字
- 前端传递的日期格式可能各不相同
- 不同接口对日期格式要求不一致
这些问题会导致:
- 前端需要手动处理时间格式转换
- 代码中充斥着大量的日期格式化代码
- 可能出现日期解析错误
解决方案
1. 全局日期格式化配置
创建配置类统一处理日期格式:
@Configuration
public class DateFormatConfig {
private static final String DATE_FORMAT = "yyyy-MM-dd";
private static final String DATE_TIME_FORMAT = "yyyy-MM-dd HH:mm:ss";
@Bean
public Jackson2ObjectMapperBuilderCustomizer jsonCustomizer() {
return builder -> {
builder.simpleDateFormat(DATE_TIME_FORMAT);
builder.serializers(new LocalDateSerializer(DateTimeFormatter.ofPattern(DATE_FORMAT)));
builder.serializers(new LocalDateTimeSerializer(DateTimeFormatter.ofPattern(DATE_TIME_FORMAT)));
builder.deserializers(new LocalDateDeserializer(DateTimeFormatter.ofPattern(DATE_FORMAT)));
builder.deserializers(new LocalDateTimeDeserializer(DateTimeFormatter.ofPattern(DATE_TIME_FORMAT)));
};
}
}2. 使用@JsonFormat注解
对于特定字段需要自定义格式时:
public class User {
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
private LocalDateTime createTime;
@JsonFormat(pattern = "yyyy-MM-dd")
private LocalDate birthday;
// getter setter
}3. 自定义日期序列化和反序列化
对于更复杂的日期处理需求:
public class CustomLocalDateTimeSerializer extends JsonSerializer<LocalDateTime> {
@Override
public void serialize(LocalDateTime value, JsonGenerator gen, SerializerProvider serializers)
throws IOException {
if (value != null) {
gen.writeString(value.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
}
}
}
public class CustomLocalDateTimeDeserializer extends JsonDeserializer<LocalDateTime> {
@Override
public LocalDateTime deserialize(JsonParser p, DeserializationContext ctxt)
throws IOException {
String dateStr = p.getText();
if (StringUtils.isEmpty(dateStr)) {
return null;
}
return LocalDateTime.parse(dateStr, DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
}
}实际应用示例
1. Controller层使用
@RestController
@RequestMapping("/api")
public class UserController {
@PostMapping("/user")
public Result<User> createUser(@RequestBody User user) {
// LocalDateTime会自动按配置格式处理
System.out.println("创建时间: " + user.getCreateTime());
System.out.println("生日: " + user.getBirthday());
return Result.success(user);
}
@GetMapping("/user/{id}")
public Result<User> getUser(@PathVariable Long id) {
User user = new User();
user.setCreateTime(LocalDateTime.now());
user.setBirthday(LocalDate.now());
// 返回的日期会自动格式化
return Result.success(user);
}
}2. 测试验证
@SpringBootTest
public class DateFormatTest {
@Autowired
private ObjectMapper objectMapper;
@Test
public void testDateFormat() throws Exception {
User user = new User();
user.setCreateTime(LocalDateTime.now());
user.setBirthday(LocalDate.now());
String json = objectMapper.writeValueAsString(user);
System.out.println("序列化结果: " + json);
User parsedUser = objectMapper.readValue(json, User.class);
System.out.println("反序列化结果: " + parsedUser);
}
}最佳实践建议
统一使用LocalDateTime和LocalDate替代Date类型
在配置文件中集中管理日期格式模式字符串
考虑时区问题,建议:
- 服务器统一使用UTC时间
- 在展示层处理时区转换
- 在配置中明确指定时区
对于特殊格式要求:
- 优先使用@JsonFormat注解
- 其次考虑自定义序列化器
添加参数校验:
public class User {
@NotNull(message = "创建时间不能为空")
private LocalDateTime createTime;
@Past(message = "生日必须是过去的日期")
private LocalDate birthday;
}常见问题
时区问题
- 现象:前后端显示时间相差几个小时
- 解决:指定正确的时区,如
timezone = "GMT+8"
格式解析异常
- 现象:日期字符串无法正确解析
- 解决:检查日期格式是否匹配,增加异常处理
性能问题
- 现象:日期转换影响接口性能
- 解决:使用缓存,避免重复创建DateTimeFormatter
总结
通过合理配置和规范使用,我们可以优雅地解决JSON日期格式问题:
- 全局配置处理通用场景
- 注解处理特殊需求
- 自定义序列化处理复杂情况
这样既保证了代码的统一性,又提供了足够的灵活性。

