Oracle JDK 17下dd MMM yyyy格式日期解析失败是否为Bug?
问题原因
这个问题不属于JDK Bug,是Java版本迭代过程中默认本地化数据源变更导致的预期行为差异:
- Java 11及更早版本默认优先使用JDK自带的兼容型本地化数据,英国英语(
Locale.UK)区域下9月的短缩写为Sep,和你代码中的字符串匹配 - 从Java 16开始,JDK默认启用Unicode CLDR(通用区域数据仓库)作为第一优先级的本地化数据源,该规则下英国英语区域9月的短缩写调整为
Sept,因此26 Sep 2021无法被正常解析,抛出索引3位置的解析异常。
你可以运行以下代码验证当前环境下的月份缩写规则:
import java.text.DateFormatSymbols; import java.util.Locale; public class CheckMonthAbbr { public static void main(String[] args) { String[] shortMonths = DateFormatSymbols.getInstance(Locale.UK).getShortMonths(); // 索引0对应1月,索引8对应9月 System.out.println("9月短缩写:" + shortMonths[8]); } }
在Oracle JDK 17环境下运行输出为Sept。
解决方案
根据你的业务场景可以选择以下任意一种方案:
- 适配新缩写规则
把代码中涉及9月的Sep字符串统一替换为Sept即可正常解析。 - 兼容旧本地化规则
启动Java应用时添加JVM参数,优先使用旧版兼容本地化数据:
该方案不需要修改业务代码,保持原有日期字符串格式即可正常运行。-Djava.locale.providers=COMPAT,CLDR - 自定义固定解析规则
如果你需要保证不同JDK版本下解析逻辑完全一致,不受系统本地化配置影响,可以通过DateTimeFormatterBuilder自定义月份缩写映射:import java.time.LocalDate; import java.time.format.DateTimeFormatter; import java.time.format.DateTimeFormatterBuilder; import java.time.temporal.ChronoField; import java.util.Locale; import java.util.Map; public class Main { public static void main(String[] args) { // 自定义固定的月份缩写映射,不受本地化数据源影响 Map<Long, String> monthAbbrMap = Map.ofEntries( Map.entry(1L, "Jan"), Map.entry(2L, "Feb"), Map.entry(3L, "Mar"), Map.entry(4L, "Apr"), Map.entry(5L, "May"), Map.entry(6L, "Jun"), Map.entry(7L, "Jul"), Map.entry(8L, "Aug"), Map.entry(9L, "Sep"), Map.entry(10L, "Oct"), Map.entry(11L, "Nov"), Map.entry(12L, "Dec") ); DateTimeFormatter formatter = new DateTimeFormatterBuilder() .appendPattern("dd ") .appendText(ChronoField.MONTH_OF_YEAR, monthAbbrMap) .appendPattern(" yyyy") .toFormatter(Locale.UK); LocalDate date = LocalDate.parse("26 Sep 2021", formatter); System.out.println(date); // 输出 2021-09-26 } }
内容的提问来源于stack exchange,提问作者Dan MacBean
相关产品推荐
相关产品推荐

