应优先使用线程安全的DateTimeFormatter并设为static final,避免SimpleDateFormat多线程共享;严格校验格式、关闭宽松解析、对齐时区与数据库类型。

SimpleDateFormat 解析字符串时抛出 ParseException 怎么办
根本原因通常是输入字符串与模式不严格匹配,比如年份少写一位、空格或多了一个字母。它对格式极其敏感,且不是线程安全的——多线程共用同一个 SimpleDateFormat 实例是常见崩溃源头。
实操建议:
立即学习“Java免费学习笔记(深入)”;
- 每次解析都新建实例,或用
ThreadLocal封装 - 确认模式字符串和输入完全对齐:
"yyyy-MM-dd"不能匹配"2023-1-1"(月份/日期缺零) - 用
setLenient(false)关闭宽松解析,避免把"2023-13-01"错误转成 2024-01-01 - 注意时区:默认使用 JVM 本地时区,若需 UTC,得显式调用
setTimeZone(TimeZone.getTimeZone("UTC"))
DateTimeFormatter.parse() 返回 LocalDateTime 还是 ZonedDateTime
取决于你用的 pattern 和输入字符串是否含时区信息。没有时区字段(如 "HH:mm:ss" 或 "yyyy-MM-dd")时,parse() 默认返回 LocalDateTime;若输入含 Z、+0800 或时区名(如 "2023-05-20T14:30:00+08:00"),则必须用 ZonedDateTime.parse() 或指定带时区的 formatter。
实操建议:
立即学习“Java免费学习笔记(深入)”;
- 用
DateTimeFormatter.ISO_OFFSET_DATE_TIME解析带偏移的时间,别硬套ISO_LOCAL_DATE_TIME - 想统一转成带时区的时间?先 parse 成
LocalDateTime,再用.atZone(ZoneId.of("Asia/Shanghai")) - 自定义 pattern 时,
XXX匹配+08:00,XX匹配+0800,X匹配+08,别混用
从字符串转日期后,为什么存进数据库时间不对
最常踩的坑是:Java 端用了 LocalDateTime(无时区),但数据库字段是 TIMESTAMP WITH TIME ZONE(如 PostgreSQL),JDBC 驱动会按 JVM 本地时区自动转换,导致值偏移。
实操建议:
立即学习“Java免费学习笔记(深入)”;
- 数据库字段类型和 Java 类型要对齐:
DATE→LocalDate,TIMESTAMP→LocalDateTime,TIMESTAMP WITH TIME ZONE→ZonedDateTime或OffsetDateTime - 用 JDBC 4.2+ 时,直接传
OffsetDateTime.now(ZoneOffset.UTC),别依赖Timestamp.valueOf(LocalDateTime) - Spring Boot + MyBatis 中,确保配置了
spring.jackson.time-zone=UTC和spring.jackson.date-format=yyyy-MM-dd HH:mm:ss,否则序列化可能悄悄加本地时区
性能对比:SimpleDateFormat vs DateTimeFormatter
DateTimeFormatter 是不可变且线程安全的,创建开销略高但复用无风险;SimpleDateFormat 创建快,但并发下必须加锁或隔离,实际吞吐反而更低。
实操建议:
立即学习“Java免费学习笔记(深入)”;
- 高频解析场景(如日志时间提取),把
DateTimeFormatter声明为static final,例如:private static final DateTimeFormatter FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss") - 不要在循环里 new
SimpleDateFormat——哪怕只用一次,也比复用一个非线程安全实例更安全 - 如果必须兼容 JDK 7,且无法升级,至少用
ThreadLocal包一层,别图省事共享实例
真正麻烦的不是语法,而是时区、leniency、JDBC 类型映射这三块的隐式行为。它们不会报错,但会在某个凌晨三点让数据差八小时。











