Java DateTimeFormatter如何实现二选一可选日期格式段解析
问题说明
需要实现DateTimeFormatter匹配规则,满足MMMM-d-(yy OR yyyy)的解析逻辑:日期字符串仅可匹配2位短年份yy或4位长年份yyyy二者其一,不允许无年份的字符串解析通过。
当前采用MMMM-d-[yyyy][yy]模式构建格式化器,测试代码如下:
val formatter = DateTimeFormatter.ofPattern("MMMM-d-[yyyy][yy]"); System.out.println(formatter.parse("June-27-2022")); // {},ISO resolved to 2022-06-27 System.out.println(formatter.parse("June-27-22")); // {},ISO resolved to 2022-06-27 System.out.println(formatter.parse("June-27-")); // {MonthOfYear=6, DayOfMonth=27},ISO
该模式可正常解析带4位年份、2位年份的日期,但存在缺陷:末尾无年份仅带横杠的June-27-也会被解析成功,两个可选年份段均未匹配时依然返回结果,不符合需求。
解决方法
DateTimeFormatter原生的模式字符串语法中,[]标记的可选段支持全部跳过,没有「一组可选段必须命中至少一个」的语法能力,纯靠拼接模式串无法实现二选一必填的逻辑,需要通过DateTimeFormatterBuilder自定义解析规则实现:
- 先追加固定的月份、日、连接横杠解析规则
- 分别将4位长年份、2位短年份的解析规则包装为可选分支顺序追加,优先匹配4位长年份,匹配失败再尝试2位短年份
- 解析时直接转换为
LocalDate类型,该类型强制要求年、月、日三个字段完整存在,缺少年份时会直接抛出解析异常
可直接使用的实现代码:
import java.time.LocalDate; import java.time.format.DateTimeFormatter; import java.time.format.DateTimeFormatterBuilder; import java.time.format.SignStyle; import static java.time.temporal.ChronoField.DAY_OF_MONTH; import static java.time.temporal.ChronoField.MONTH_OF_YEAR; import static java.time.temporal.ChronoField.YEAR; DateTimeFormatter formatter = new DateTimeFormatterBuilder() .appendPattern("MMMM-d-") // 4位长年份匹配分支 .appendOptional( new DateTimeFormatterBuilder() .appendValue(YEAR, 4, 4, SignStyle.NORMAL) .toFormatter() ) // 2位短年份匹配分支,基准年可根据业务需求调整 .appendOptional( new DateTimeFormatterBuilder() .appendValueReduced(YEAR, 2, 2, 2000) .toFormatter() ) .toFormatter();
测试验证结果:
- 解析
June-27-2022:正常转换为LocalDate对象,对应日期2022-06-27 - 解析
June-27-22:按配置的2000基准年规则,正常转换为LocalDate对象,对应日期2022-06-27 - 解析
June-27-:直接抛出DateTimeParseException,符合预期
注意:2位短年份的解析基准年可根据实际业务场景调整,比如设置为1970则
70会解析为1970,23会解析为2023。
内容的提问来源于stack exchange,提问作者alturkovic
相关产品推荐
相关产品推荐

