How to Simplify Date Formatting in Spring Boot: Global Config & @JsonFormat Tricks
This article shares practical techniques for handling date and time in Spring Boot projects, including the pitfalls of using SimpleDateFormat, the benefits of @JsonFormat, and two approaches to global configuration that streamline formatting for both java.util.Date and java.time types.
Preface
After several senior colleagues left the department, I inherited their projects and immediately noticed a recurring problem: almost every date formatting operation used SimpleDateFormat, leading to verbose and error‑prone code.
A Pitfall
Our monthly code‑review sessions often require us to optimize others' code. Refactoring date handling is especially painful because many modules still rely on java.util.Date and java.util.Calendar, which are not thread‑safe and lack time‑zone support. The team now mandates the use of the JDK 1.8+ java.time API, such as LocalDateTime, but legacy code persists.
SvcOrderDailyStatisticsPo orderDailyStatisticsPo = new SvcOrderDailyStatisticsPo();
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
Date stationTime = dateFormat.parse(dateFormat.format(svcWorkOrderPo.getPayEndTime()));
orderDailyStatisticsPo.setStatisticalDate(stationTime);Mixing java.util.Date, java.util.Calendar, and the newer java.time types makes calculations cumbersome.
Optimization Strategies
Because date formatting is used so frequently, we should abstract it into a global utility. Below are several practical solutions demonstrated with code.
Test endpoint:
http://127.0.0.1:8080/timeTest @GetMapping("/timeTest")
public OrderInfo timeTest() {
OrderInfo order = new OrderInfo();
order.setCreateTime(LocalDateTime.now());
order.setUpdateTime(new Date());
return order;
}1. Using @JsonFormat Annotation
The @JsonFormat annotation is a common, straightforward way to control JSON serialization of date fields.
public class OrderInfo {
@JsonFormat(locale = "zh", timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime createTime;
@JsonFormat(locale = "zh", timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss")
private Date updateTime;
public LocalDateTime getCreateTime() { return createTime; }
public void setCreateTime(LocalDateTime createTime) { this.createTime = createTime; }
public Date getUpdateTime() { return updateTime; }
public void setUpdateTime(Date updateTime) { this.updateTime = updateTime; }
}While this works, it requires adding the annotation to every date field, which can be repetitive.
2. Global Configuration (1)
Spring Boot already provides a property ${spring.jackson.date-format:yyyy-MM-dd HH:mm:ss}. By defining a configuration class that registers a LocalDateTimeSerializer bean, we can apply this format globally without annotating each field.
public class OrderInfo {
// @JsonFormat(...) // not needed after global config
private LocalDateTime createTime;
private Date updateTime;
// getters and setters omitted for brevity
} @Configuration
public class LocalDateTimeSerializerConfig {
@Value("${spring.jackson.date-format:yyyy-MM-dd HH:mm:ss}")
private String pattern;
@Bean
public LocalDateTimeSerializer localDateTimeDeserializer() {
return new LocalDateTimeSerializer(DateTimeFormatter.ofPattern(pattern));
}
@Bean
public Jackson2ObjectMapperBuilderCustomizer jackson2ObjectMapperBuilderCustomizer() {
return builder -> builder.serializerByType(LocalDateTime.class, localDateTimeDeserializer());
}
}This configuration supports both Date and LocalDateTime. However, some fields may require a different pattern such as yyyy-MM-dd, which can be handled by adding @JsonFormat on those specific fields.
3. Global Configuration (2)
A similar global setup can be created with a primary ObjectMapper. After applying this configuration, the @JsonFormat annotation on individual fields no longer takes effect.
public class OrderInfo {
private LocalDateTime createTime;
private Date updateTime;
// getters and setters omitted
} @Configuration
public class LocalDateTimeSerializerConfig {
@Value("${spring.jackson.date-format:yyyy-MM-dd HH:mm:ss}")
private String pattern;
@Bean
@Primary
public ObjectMapper serializingObjectMapper() {
ObjectMapper objectMapper = new ObjectMapper();
JavaTimeModule javaTimeModule = new JavaTimeModule();
javaTimeModule.addSerializer(LocalDateTime.class, new LocalDateTimeSerializer());
javaTimeModule.addDeserializer(LocalDateTime.class, new LocalDateTimeDeserializer());
objectMapper.registerModule(javaTimeModule);
return objectMapper;
}
public class LocalDateTimeSerializer extends JsonSerializer<LocalDateTime> {
@Override
public void serialize(LocalDateTime value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
gen.writeString(value.format(DateTimeFormatter.ofPattern(pattern)));
}
}
public class LocalDateTimeDeserializer extends JsonDeserializer<LocalDateTime> {
@Override
public LocalDateTime deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
return LocalDateTime.parse(p.getValueAsString(), DateTimeFormatter.ofPattern(pattern));
}
}
}Conclusion
The post shares a practical Spring Boot tip for handling date and time globally, reflects on the challenges of optimizing legacy code, and demonstrates how such refactoring can improve development efficiency and personal skill growth.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
macrozheng
Dedicated to Java tech sharing and dissecting top open-source projects. Topics include Spring Boot, Spring Cloud, Docker, Kubernetes and more. Author’s GitHub project “mall” has 50K+ stars.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
