java.time月份拼写Bug规避方案咨询:September显示为Sept问题
JDK
java.time 9月缩写Bug的规避方案 问题背景
在JDK 17.0.10、JDK 21.0.4版本(涵盖MacOS X和Centos Stream 9系统)中,使用DateTimeFormatter.ofPattern("EEE MMM dd HH:mm:ss yyyy")格式化9月日期时,会错误输出Sept而非预期的Sep;反序列化标准Sep格式字符串时,会抛出DateTimeParseException,导致Gson反序列化失败。该Bug已上报至Oracle Bug数据库(ID:9077542),短期内无法修复,需手动规避。
可行规避方案
方案1:自定义月份缩写映射的DateTimeFormatter
通过DateTimeFormatterBuilder手动指定所有月份的缩写,强制9月使用Sep,从根源上统一格式化与解析行为:
public class EuDateTimeDeserializer implements JsonDeserializer<LocalDateTime> { // 自定义月份缩写映射 private static final Map<Long, String> MONTH_ABBREVIATIONS = new HashMap<>(); static { MONTH_ABBREVIATIONS.put(1L, "Jan"); MONTH_ABBREVIATIONS.put(2L, "Feb"); MONTH_ABBREVIATIONS.put(3L, "Mar"); MONTH_ABBREVIATIONS.put(4L, "Apr"); MONTH_ABBREVIATIONS.put(5L, "May"); MONTH_ABBREVIATIONS.put(6L, "Jun"); MONTH_ABBREVIATIONS.put(7L, "Jul"); MONTH_ABBREVIATIONS.put(8L, "Aug"); MONTH_ABBREVIATIONS.put(9L, "Sep"); // 强制指定9月缩写为Sep MONTH_ABBREVIATIONS.put(10L, "Oct"); MONTH_ABBREVIATIONS.put(11L, "Nov"); MONTH_ABBREVIATIONS.put(12L, "Dec"); } private static final DateTimeFormatter CUSTOM_FORMATTER = new DateTimeFormatterBuilder() .appendPattern("EEE ") .appendText(ChronoField.MONTH_OF_YEAR, MONTH_ABBREVIATIONS) .appendPattern(" dd HH:mm:ss yyyy") .toFormatter(Locale.ENGLISH); // 指定Locale,避免系统默认设置影响 @Override public LocalDateTime deserialize(JsonElement jsonElement, Type type, JsonDeserializationContext context) throws JsonParseException { String dateTimeStr = jsonElement.getAsString(); if (!ThssUtils.isValidString(dateTimeStr)) { return null; } return LocalDateTime.parse(dateTimeStr, CUSTOM_FORMATTER); } // 格式化输出时也使用该自定义Formatter,保证输出一致性 public static String format(LocalDateTime dateTime) { return CUSTOM_FORMATTER.format(dateTime); } }
方案2:字符串预处理兼容两种缩写
在解析前对日期字符串进行替换,同时兼容Sep和Sept两种格式;格式化输出时也修正错误的Sept:
public class EuDateTimeDeserializer implements JsonDeserializer<LocalDateTime> { private static final DateTimeFormatter FORMATTER = DateTimeFormatter.ofPattern("EEE MMM dd HH:mm:ss yyyy", Locale.ENGLISH); @Override public LocalDateTime deserialize(JsonElement jsonElement, Type type, JsonDeserializationContext context) throws JsonParseException { String dateTimeStr = jsonElement.getAsString(); if (!ThssUtils.isValidString(dateTimeStr)) { return null; } // 替换Sept为Sep,兼容两种格式 String processedStr = dateTimeStr.replace("Sept", "Sep"); return LocalDateTime.parse(processedStr, FORMATTER); } // 格式化时修正Bug导致的错误输出 public static String format(LocalDateTime dateTime) { String formatted = FORMATTER.format(dateTime); return formatted.replace("Sept", "Sep"); } }
方案3:使用可选模式匹配两种缩写
通过DateTimeFormatterBuilder添加可选的月份缩写模式,同时支持Sep和Sept两种格式:
public class EuDateTimeDeserializer implements JsonDeserializer<LocalDateTime> { private static final DateTimeFormatter FORMATTER = new DateTimeFormatterBuilder() .appendPattern("EEE ") .appendOptional(DateTimeFormatter.ofPattern("Sep")) .appendOptional(DateTimeFormatter.ofPattern("Sept")) .appendPattern(" dd HH:mm:ss yyyy") .toFormatter(Locale.ENGLISH); @Override public LocalDateTime deserialize(JsonElement jsonElement, Type type, JsonDeserializationContext context) throws JsonParseException { String dateTimeStr = jsonElement.getAsString(); if (!ThssUtils.isValidString(dateTimeStr)) { return null; } return LocalDateTime.parse(dateTimeStr, FORMATTER); } }
注:需注意模式顺序,优先匹配3字符缩写以避免解析歧义。
总结
优先推荐方案1,它从根源上统一了月份缩写格式,避免字符串替换可能带来的意外问题;如果仅需临时兼容现有数据,方案2实现更简单、成本更低。
内容的提问来源于stack exchange,提问作者cidy.long
相关产品推荐
相关产品推荐

