datetimeformatterbuilder 通过 optionalstart()/optionalend() 构建可选字段的嵌套结构,支持灵活解析与格式化;需严格匹配嵌套、配合 parsedefaulting 设置默认值,并建议为不同场景创建专用 formatter。

DateTimeFormatterBuilder 是 Java 8+ 中构建自定义日期时间格式器的灵活工具,特别适合处理带可选部分(比如“AM/PM”、时区缩写、毫秒后缀等)的复杂日期字符串。关键在于用 optionalStart() 和 optionalEnd() 包裹可选内容,并注意嵌套顺序与解析优先级。
用 optionalStart() / optionalEnd() 包裹可选字段
这是组合可选后缀的核心机制。所有被包裹的内容在解析时会被尝试匹配,但不匹配也不报错;格式化时则只在值存在时输出。
- 例如:解析 "2023-10-05 14:30" 或 "2023-10-05 14:30:45.123" 或 "2023-10-05 14:30:45.123 PST"
- 写法示例:
new DateTimeFormatterBuilder()
.appendPattern("yyyy-MM-dd HH:mm")
.optionalStart()
.appendPattern(":ss")
.optionalStart()
.appendFraction(ChronoField.NANO_OF_SECOND, 0, 9, true)
.optionalEnd()
.optionalEnd()
.optionalStart()
.appendZoneText(TextStyle.SHORT)
.optionalEnd()
.toFormatter();
避免嵌套冲突:optional 区域不能交叉或重叠
每个 optionalStart() 必须有对应 optionalEnd(),且不能相互交叉。错误写法会导致解析异常或行为不可预期。
- ✅ 正确:外层包裹秒,内层包裹纳秒 → 支持 “mm:ss”、“mm:ss.SSS” 两种形式
- ❌ 错误:先 optionalStart() 秒,再 optionalStart() 时区,然后只 close 一次 → 编译不报错但运行时解析失败
- 提示:用缩进对齐 optional 范围,或拆成多个 builder 链式调用提升可读性
配合 parseDefaulting 处理缺失字段的默认值
当可选部分未出现时,某些字段(如秒、纳秒、时区)可能为 null,导致解析失败。可用 parseDefaulting() 提供默认值。
- 例如:允许 "2023-10-05 14:30" 解析为 LocalDateTime,但想转成 ZonedDateTime,需补默认时区
- 写法示例:
.parseDefaulting(ChronoField.SECOND_OF_MINUTE, 0)
.parseDefaulting(ChronoField.NANO_OF_SECOND, 0)
.parseDefaulting(ChronoField.OFFSET_SECONDS, ZoneOffset.UTC.getTotalSeconds()) - 注意:parseDefaulting 只影响解析,不影响格式化输出
区分解析与格式化:可选内容在 format 时不自动跳过
formatter.format() 会按构造逻辑输出所有内容——包括 optional 区域,只要字段值非空或默认存在。若希望格式化时也“按需省略”,需提前判断或使用多个 formatter。
- 例如:你有一个 Instant,想格式化为 “yyyy-MM-dd HH:mm”(无秒)或 “yyyy-MM-dd HH:mm:ss”(有秒),就得根据秒是否为 0 选择不同 formatter
- 更稳妥做法:用 DateTimeFormatterBuilder 构建多个专用 formatter(如 basic、withSecond、withZone),运行时按需选用
- 不推荐在单个 formatter 中靠“值为 0 就跳过”实现动态省略——Java 原生不支持这种条件格式化逻辑
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











