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.Dateand
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.timeAPI, such as
LocalDateTime, but legacy code persists.
<code>SvcOrderDailyStatisticsPo orderDailyStatisticsPo = new SvcOrderDailyStatisticsPo();
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
Date stationTime = dateFormat.parse(dateFormat.format(svcWorkOrderPo.getPayEndTime()));
orderDailyStatisticsPo.setStatisticalDate(stationTime);
</code>Mixing
java.util.Date,
java.util.Calendar, and the newer
java.timetypes 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 <code>@GetMapping("/timeTest")
public OrderInfo timeTest() {
OrderInfo order = new OrderInfo();
order.setCreateTime(LocalDateTime.now());
order.setUpdateTime(new Date());
return order;
}
</code>1. Using @JsonFormat Annotation
The
@JsonFormatannotation is a common, straightforward way to control JSON serialization of date fields.
<code>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; }
}
</code>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
LocalDateTimeSerializerbean, we can apply this format globally without annotating each field.
<code>public class OrderInfo {
// @JsonFormat(...) // not needed after global config
private LocalDateTime createTime;
private Date updateTime;
// getters and setters omitted for brevity
}
</code> <code>@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());
}
}
</code>This configuration supports both
Dateand
LocalDateTime. However, some fields may require a different pattern such as
yyyy-MM-dd, which can be handled by adding
@JsonFormaton those specific fields.
3. Global Configuration (2)
A similar global setup can be created with a primary
ObjectMapper. After applying this configuration, the
@JsonFormatannotation on individual fields no longer takes effect.
<code>public class OrderInfo {
private LocalDateTime createTime;
private Date updateTime;
// getters and setters omitted
}
</code> <code>@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));
}
}
}
</code>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.
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.