datetimeformatter线程安全且不可变,应避免simpledateformat思维;优先用iso预定义常量,自定义pattern需严守大小写与数量规则;解析须匹配具体类型,格式化前需手动处理时区。

DateTimeFormatter 是 Java 8+ 中线程安全、不可变的日期格式化工具,直接用它做互转没问题,但必须避开 SimpleDateFormat 那套思维——它不共享状态,也不需要 synchronized,但对模式字符串敏感,错一个字符就抛 DateTimeParseException。
用预定义常量快速格式化常见场景
多数业务不需要手写 pattern,DateTimeFormatter 提供了几个开箱即用的静态常量,比如 ISO_LOCAL_DATE、ISO_LOCAL_DATE_TIME,它们严格遵循 ISO 8601,解析和格式化都可靠:
-
LocalDate.parse("2024-05-20", DateTimeFormatter.ISO_LOCAL_DATE)能成功 -
LocalDateTime.parse("2024-05-20T14:30:45", DateTimeFormatter.ISO_LOCAL_DATE_TIME)也能成功 - 但
"2024/05/20"或"2024-05-20 14:30"这类非 ISO 格式,直接用预定义常量会抛DateTimeParseException
这类常量适合 API 交互、日志时间戳等强调标准性的场景,省心且无兼容性风险。
自定义 pattern 时必须注意字母大小写和数量
pattern 字符不是随意写的:y 和 Y 含义不同,M 和 MM 解析行为也不同。常见踩坑点:
-
yyyy-MM-dd:年份用小写y(基于年份,非周年的),月份用大写M(1–12),日期用小写d(1–31) -
YYYY-MM-dd:大写Y表示“基于周的年”,在跨年周(如 2024-12-30 属于 2025 年第 1 周)时会出错,日常几乎不用 -
HH:mm:ss对应 24 小时制;hh:mm:ss a才支持AM/PM,且必须配a,否则解析"02:30:45 PM"失败 - 单个
M可解析"2024-5-20",但MM要求两位,"2024-5-20"会报错
解析字符串时要匹配具体类型(LocalDate / LocalDateTime / ZonedDateTime)
不能拿一个字符串硬塞给所有类型解析。例如:
-
"2024-05-20"只含日期 → 用LocalDate.parse(str, formatter) -
"2024-05-20 14:30:45"含日期+时间但无时区 → 先用LocalDateTime.parse(str, formatter),再根据需要转成ZonedDateTime -
"2024-05-20T14:30:45+08:00"含偏移 → 必须用ZonedDateTime.parse(str, formatter),用LocalDateTime会丢时区信息或直接失败
类型不匹配是 DateTimeParseException 最常见来源,别指望自动推导。
格式化时注意时区和系统默认行为差异
DateTimeFormatter 本身不带时区,但你要格式化的对象可能带。比如:
-
LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm"))输出的是系统默认时区下的“本地时间”,但没显式时区标识 -
ZonedDateTime.now(ZoneId.of("Asia/Shanghai")).format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSSXXX"))才能输出带+08:00的结果 - 如果只想要 UTC 时间,得先调用
withZoneSameInstant(ZoneOffset.UTC),而不是靠 formatter “猜”
最容易被忽略的是:formatter 不负责时区转换,只负责把已有的时间对象按规则转成字符串。该换时区,得在 format 前做完。










