SimpleDateFormat解析字符串为Date需严格匹配模式且线程不安全;常见ParseException因模式与输入不一致、分隔符错误、年份位数不符或默认严格模式拒绝非法日期;应避免共享实例,推荐ThreadLocal封装或Java 8+ DateTimeFormatter。

SimpleDateFormat 能解析字符串为 Date,但必须严格匹配模式,且线程不安全——别在多线程环境里复用同一个实例。
为什么 parse() 会抛出 ParseException?
最常见原因是输入字符串和 SimpleDateFormat 的模式不一致。比如模式是 "yyyy-MM-dd",却传入 "2023/05/10" 或 "2023-05-10 14:30",都会失败。
- 检查年份位数:模式用
yy时,"23"可以;用yyyy时,"23"就不行 - 注意分隔符:模式中是
-,字符串里用了/或空格,直接报错 - 默认不宽松:即使日期逻辑合理(如
"2023-02-30"),默认也会拒绝——可通过setLenient(false)显式关闭宽松模式,但通常建议保持false防止隐式修正
如何安全地复用 SimpleDateFormat 实例?
不能直接在类字段或静态变量里共享一个 SimpleDateFormat 实例,尤其在 Web 应用或线程池中。它内部维护可变状态(如 calendar 字段),并发调用 parse() 或 format() 会导致结果错乱甚至 NullPointerException。
你接手一个需求时,是不是经常这样:"改一个小字段 → 结果把不相关的代码也改了"、"需求说得很模糊 → 做着做着发现跑偏了"、"数据库加个字段 → 上线才发现漏了数据兼容"、"做了个新功能 → 一个月后自己都忘了当时怎么设计的"、"写到一半被中断 → 再回来不知道从哪继续";这个 Skill 就是专门解决这些问题的。 它会自动探测你的项目环境(JDK、Spring Boot、ORM、数据库、前端框架),记在
- 方法一:每次用都新建(适合低频场景)
new SimpleDateFormat("HH:mm:ss").parse("14:25:30") - 方法二:用
ThreadLocal封装private static final ThreadLocal<SimpleDateFormat> TIME_FORMAT = ThreadLocal.withInitial(() -> new SimpleDateFormat("HH:mm:ss"));取用时调TIME_FORMAT.get().parse(...) - 方法三:改用 Java 8+ 的
DateTimeFormatter+LocalDateTime/ZonedDateTime,线程安全且更清晰
SimpleDateFormat 解析后 Date 的时区怎么算?
Date 本身不存时区,只存自 Unix epoch 起的毫秒数;但 SimpleDateFormat 在解析时会按其 TimeZone 解释输入字符串。默认使用 JVM 本地时区,容易造成误解。
立即学习“Java免费学习笔记(深入)”;
- 如果字符串含时区信息(如
"2023-05-10T14:30:00+0800"),模式需包含z或X,且SimpleDateFormat会据此调整时间戳 - 若字符串无时区(如
"2023-05-10"),而你希望按 UTC 解析,必须显式设置:SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");<br>sdf.setTimeZone(TimeZone.getTimeZone("UTC")); - 否则,同一字符串在不同时区的机器上解析出的
Date毫秒值不同
真正麻烦的不是写对一行 parse(),而是确保模式、输入、时区、线程上下文四者始终对齐——漏掉任一环,问题就藏在看似正常的返回值里。


















