如何阻止Java 8 DateTimeFormatter自动补全秒字段为0?
如何让Java DateTimeFormatter解析后不自动补全缺失的时间字段?
我想要创建一个格式化器,解析字符串后生成不包含秒字段的TemporalAccessor,这样当用需要秒字段的格式器输出时会直接失败。但实际测试中,缺失的秒字段被自动补成了0,和预期不符。
代码示例
DateTimeFormatter partialDateTimeFormatter = new DateTimeFormatterBuilder() .appendPattern("yyyy-MM-d'T'HH:mm") .toFormatter(); DateTimeFormatter fullDateTimeFormatter = new DateTimeFormatterBuilder() .appendPattern("yyyy-MM-d'T'HH:mm:ss.SSS") .toFormatter(); TemporalAccessor ta = partialDateTimeFormatter.parse("2020-01-02T06:11"); LOGGER.info("seconds supported: {}",ta.isSupported(ChronoField.SECOND_OF_MINUTE)); LOGGER.info("fullDateTimeFormatter: {}",fullDateTimeFormatter.format(ta));
实际输出
12:44:02.561 [main] INFO MatcherTest - seconds supported: true 12:39:44.370 [main] INFO MatcherTest - fullDateTimeFormatter: 2020-01-2T06:11:00.000
问题原因
官方文档提到:"若至少提供了一天中的小时数,则会生成LocalTime。这会为分钟、秒和毫秒提供默认值。" 也就是说,默认的解析逻辑会自动生成完整的LocalTime(甚至LocalDateTime)实例,这类实例会为所有缺失的时间字段填充默认值(秒、毫秒默认0),导致后续格式化时能正常输出,不符合我们想要的"缺失字段就失败"的预期。
解决方案
要阻止这种自动补全,需要切换到严格解析模式,并避免解析器生成完整的时间对象,只保留解析到的字段集合:
方法一:使用严格解析器风格
DateTimeFormatter partialDateTimeFormatter = new DateTimeFormatterBuilder() .appendPattern("yyyy-MM-d'T'HH:mm") .parseStrict() // 开启严格解析,拒绝自动补全 .toFormatter() .withResolverStyle(ResolverStyle.STRICT); // 严格解析模式,不填充默认值 DateTimeFormatter fullDateTimeFormatter = new DateTimeFormatterBuilder() .appendPattern("yyyy-MM-d'T'HH:mm:ss.SSS") .toFormatter(); TemporalAccessor ta = partialDateTimeFormatter.parse("2020-01-02T06:11"); LOGGER.info("seconds supported: {}", ta.isSupported(ChronoField.SECOND_OF_MINUTE)); // 此行会抛出 DateTimeException,因为 ta 不包含秒字段 LOGGER.info("fullDateTimeFormatter: {}", fullDateTimeFormatter.format(ta));
这种方式下,解析得到的TemporalAccessor仅包含解析到的年、月、日、时、分字段,isSupported(ChronoField.SECOND_OF_MINUTE)会返回false,当用需要秒字段的格式器格式化时,直接抛出java.time.DateTimeException: Unable to extract value: ChronoField.SECOND_OF_MINUTE,符合预期。
方法二:自定义解析器(更灵活)
如果需要更细粒度的控制,可以自定义解析逻辑,手动返回仅包含已解析字段的TemporalAccessor:
DateTimeFormatter partialDateTimeFormatter = new DateTimeFormatterBuilder() .appendPattern("yyyy-MM-d'T'HH:mm") .toFormatter() .withResolver((fields, context, style) -> new TemporalAccessor() { @Override public boolean isSupported(TemporalField field) { return fields.containsKey(field); } @Override public long getLong(TemporalField field) { if (!isSupported(field)) { throw new DateTimeException("字段不支持: " + field); } return fields.get(field); } });
这种方式完全由你控制哪些字段被支持,确保缺失的字段不会被自动补全,格式化时只要用到未支持的字段就会抛出异常。
内容的提问来源于stack exchange,提问作者The Shoe Shiner
相关产品推荐
相关产品推荐

