Skip to content

配置全局日期和时间格式 (Configuring a Global Date and Time Format)

默认情况下,未标注 @DateTimeFormat 的日期和时间字段使用 DateFormat.SHORT 样式从字符串转换。如果需要,你可以通过定义自己的全局格式来更改此行为。

为此,请确保 Spring 不注册默认格式化器。相反,请借助以下辅助类手动注册格式化器:

  • org.springframework.format.datetime.standard.DateTimeFormatterRegistrar (用于 JSR-310)
  • org.springframework.format.datetime.DateFormatterRegistrar (用于 legacy Date)

例如,以下配置注册了全局的 yyyyMMdd 格式:

java
@Configuration
public class ApplicationConfiguration {

	@Bean
	public FormattingConversionService conversionService() {

		// 使用 DefaultFormattingConversionService 但不注册默认值
		DefaultFormattingConversionService conversionService =
				new DefaultFormattingConversionService(false);

		// 确保 @NumberFormat 仍然受支持
		conversionService.addFormatterForFieldAnnotation(
				new NumberFormatAnnotationFormatterFactory());

		// 注册具有特定全局格式的 JSR-310 日期转换
		DateTimeFormatterRegistrar dateTimeRegistrar = new DateTimeFormatterRegistrar();
		dateTimeRegistrar.setDateFormatter(DateTimeFormatter.ofPattern("yyyyMMdd"));
		dateTimeRegistrar.registerFormatters(conversionService);

		// 注册具有特定全局格式的日期转换
		DateFormatterRegistrar dateRegistrar = new DateFormatterRegistrar();
		dateRegistrar.setFormatter(new DateFormatter("yyyyMMdd"));
		dateRegistrar.registerFormatters(conversionService);

		return conversionService;
	}
}
kotlin
@Configuration
class ApplicationConfiguration {

	@Bean
	fun conversionService(): FormattingConversionService {
		// 使用 DefaultFormattingConversionService 但不注册默认值
		return DefaultFormattingConversionService(false).apply {

			// 确保 @NumberFormat 仍然受支持
			addFormatterForFieldAnnotation(NumberFormatAnnotationFormatterFactory())

			// 注册具有特定全局格式的 JSR-310 日期转换
			val dateTimeRegistrar = DateTimeFormatterRegistrar()
			dateTimeRegistrar.setDateFormatter(DateTimeFormatter.ofPattern("yyyyMMdd"))
			dateTimeRegistrar.registerFormatters(this)

			// 注册具有特定全局格式的日期转换
			val dateRegistrar = DateFormatterRegistrar()
			dateRegistrar.setFormatter(DateFormatter("yyyyMMdd"))
			dateRegistrar.registerFormatters(this)
		}
	}
}
xml
<bean id="conversionService" class="org.springframework.format.support.FormattingConversionServiceFactoryBean">
	<property name="registerDefaultFormatters" value="false" />
	<property name="formatters">
		<set>
			<bean class="org.springframework.format.number.NumberFormatAnnotationFormatterFactory" />
		</set>
	</property>
	<property name="formatterRegistrars">
		<set>
			<bean class="org.springframework.format.datetime.standard.DateTimeFormatterRegistrar">
				<property name="dateFormatter">
					<bean class="org.springframework.format.datetime.standard.DateTimeFormatterFactoryBean">
						<property name="pattern" value="yyyyMMdd"/>
					</bean>
				</property>
			</bean>
		</set>
	</property>
</bean>

请注意,在 Web 应用程序中配置日期和时间格式时,还有额外的注意事项。请参阅 WebMVC 转换与格式化WebFlux 转换与格式化


补充教学

1. 为什么要显式关闭默认注册?

在手动配置全局格式时,通过 new DefaultFormattingConversionService(false) 关闭默认注册非常关键。

  • 避免冲突:默认情况下,Spring 会注册一套基于区域设置(Locale)的默认解析规则。如果不关闭,你自定义的全局格式可能无法在某些情况下生效,或者因为优先级问题导致不可预知的解析错误。
  • 可控性:手动注册允许你明确指定哪些注解(如 @NumberFormat)被保留,同时完全控制日期解析器的行为。

2. JSR-310 与 Legacy Date 的双重配置

上面的示例同时配置了 DateTimeFormatterRegistrar(处理 Java 8+ 的 LocalDate, LocalDateTime)和 DateFormatterRegistrar(处理旧的 java.util.Date)。 在现代项目中,虽然推荐全量使用 JSR-310,但为了兼容某些老旧库或数据库驱动,同时配置两者是最稳妥的做法。

3. Spring Boot 的简化方案

虽然上述代码展示了 Spring Framework 的底层配置方式,但在 Spring Boot 中,你通常可以通过配置文件直接达成目标,而无需编写如此复杂的 Java 代码:

properties
# Spring Boot 全局日期格式化配置
spring.mvc.format.date=yyyyMMdd
spring.mvc.format.date-time=yyyy-MM-dd HH:mm:ss

底层原理实际上是 Spring Boot 的自动配置类为你执行了类似于上述 DateTimeFormatterRegistrar 的操作。

4. 国际化 (i18n) 的权衡

全局格式化是一把双刃剑:

  • 优点:全应用统一,前后端对接简单,不需要在每个字段上写 @DateTimeFormat
  • 缺点:强制抹平了地区差异。如果你的应用需要支持多国语言,且不同国家习惯的日期格式不同(如美国 MM/dd/yyyy vs 中国 yyyy/MM/dd),则不应使用这种强硬的全局 pattern,而应依赖默认的基于 Locale 的解析机制或在必要处局部使用注解。

Based on Spring Framework.