如何稳定ThreeTenBp下DateTimeFormatter#ofLocalizedDateTime的不稳定测试?
ThreeTenBp 本地化日期格式化跨环境测试偶发失败问题
核心现象
基于ThreeTenBp封装的SHORT级日期时间格式化逻辑,在不同运行环境下输出结果存在差异,导致单元测试偶发失败:
- 测试预期匹配值:
1/22/19 1:00 AM - 测试实际输出值:
1/22/19, 1:00 AM - 测试前置规则:将系统时区固定为GMT+1、系统语言固定为Locale.US,对时间戳
1548115200000L做格式化后,和预期字符串做严格相等断言。
实测关联因素的复现结果
问题表现完全和当前JDK运行版本绑定,和IDE/终端运行环境无直接关系:
- Ubuntu 20.04 终端(配置
LANG=en_US.UTF-8)下:JAVA_HOME指向Java 8 OpenJDK路径:测试可正常通过JAVA_HOME指向Java 11 OpenJDK路径:测试失败,输出带逗号的拼接格式JAVA_HOME指向Java 14 OpenJDK路径:Gradle测试进程异常退出,返回非零退出码134
- Android Studio 4.2 Beta 3/2020.3.1 Canary 4环境下:
- 使用IDE默认内置JDK 11时,测试失败
- 通过
File > Project Structure > SDK location将项目关联JDK切换为本地JDK 8后,测试可正常通过
问题本质
Java 9 及后续版本对内置CLDR(Unicode通用语言环境数据库)规则的调整是导致格式差异的核心原因:
- ThreeTenBp作为Java 8时间API的兼容backport库,
ofLocalizedDateTime()方法生成的格式化器,本身不内置固定的规则集,会直接读取当前JVM加载的系统语言环境资源生成格式化模板。 - Java 8及更早版本的内置CLDR规则中,英语(美国)地区的SHORT级日期时间拼接规则为无逗号分隔:
[日期] [时间]。 - Java 9及后续版本升级了内置CLDR版本,将同场景下的拼接规则调整为带逗号分隔:
[日期], [时间],直接导致同一段业务代码在不同JDK版本下输出不一致。
规避方案
- 对输出格式有明确要求的业务场景,不要依赖
ofLocalizedDateTime()的隐式规则,直接通过DateTimeFormatter.ofPattern("M/d/yy h:mm a", Locale.US)显式定义固定格式化模板,彻底规避CLDR版本差异的影响。 - 编写单元测试时,不要对本地化格式化结果做严格字符串相等断言,兼容不同JDK版本下的合法格式变更。
- 需要使用本地化能力的场景,在格式化器初始化时主动绑定固定版本的CLDR资源,避免随JDK运行环境动态切换规则。
内容的提问来源于stack exchange,提问作者JJD
相关产品推荐
相关产品推荐

