使用java.time类解析省略分钟的日期时间输入字符串
解决省略分钟的类ISO 8601字符串解析问题
这种数据源格式不规范的情况太常见了,默认的DateTimeFormatter.ISO_LOCAL_DATE_TIME确实搞不定——因为它要求分钟是必填项。不过我们可以自定义格式化器,轻松让缺失的分钟自动填充为0,同时还能兼容标准ISO 8601格式。
方法一:用带可选片段的模式字符串
Java的DateTimeFormatter支持用方括号[]标记可选的格式部分,我们可以直接定义一个兼容多种场景的模式:
// 兼容"yyyy-MM-dd'T'HH"、"yyyy-MM-dd'T'HH:mm"、标准ISO格式的格式化器 DateTimeFormatter customFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH[:mm[:ss[.SSS]]]"); // 测试解析缺分钟的输入 String input = "2018-01-23T12"; LocalDateTime dateTime = LocalDateTime.parse(input, customFormatter); System.out.println(dateTime); // 输出:2018-01-23T12:00 // 也能完美兼容带分钟/秒的标准格式 String standardInput = "2018-01-23T12:34:56.123"; LocalDateTime standardDateTime = LocalDateTime.parse(standardInput, customFormatter); System.out.println(standardDateTime); // 输出:2018-01-23T12:34:56.123000000
这里的[:mm]表示分钟部分是可选的,输入里没有的话就自动填充0;后面的秒和毫秒片段也是可选的,完全适配标准ISO 8601的各种变体。
方法二:用DateTimeFormatterBuilder构建(逻辑更直观)
如果想更清晰地控制每一部分的可选性,推荐用DateTimeFormatterBuilder逐步构建格式化器:
DateTimeFormatter customFormatter = new DateTimeFormatterBuilder() // 复用ISO标准的日期解析规则 .append(DateTimeFormatter.ISO_LOCAL_DATE) .appendLiteral('T') // 小时部分是必填项 .appendHour(2) // 标记后续为可选的分钟部分 .optionalStart() .appendLiteral(':') .appendMinute(2) // 标记后续为可选的秒部分 .optionalStart() .appendLiteral(':') .appendSecond(2) // 标记后续为可选的小数秒部分 .optionalStart() .appendFraction(ChronoField.NANO_OF_SECOND, 0, 9, true) .toFormatter();
这种方式的优势是逻辑一目了然,每一个optionalStart()都明确标记了后续片段的可选性,同样能实现缺分钟自动补0的效果。
为什么默认的ISO_LOCAL_DATE_TIME不行?
默认的ISO_LOCAL_DATE_TIME对应的模式是yyyy-MM-dd'T'HH:mm[:ss[.SSS]],其中分钟mm是强制必填的,所以当输入缺少分钟时,会直接抛出DateTimeParseException。我们自定义的格式化器把分钟也设为可选,就完美解决了这个数据源的异常问题。
内容的提问来源于stack exchange,提问作者Basil Bourque




