SimpleDateFormat线程不安全,禁用静态复用;pattern大小写敏感(如yyyy≠YYYY、mm≠MM);务必设setLenient(false)并捕获ParseException;新项目优先用线程安全的DateTimeFormatter。

SimpleDateFormat 线程不安全,别在多线程里复用同一个实例
这是最常踩的坑:把 SimpleDateFormat 声明为静态变量或单例,在并发环境下解析或格式化会返回错误时间,甚至抛出 java.lang.NumberFormatException 或乱序结果。
- 根本原因:内部使用了共享的
Calendar实例和字符缓冲区,没有同步保护 - 正确做法:每次使用都新建对象,或改用线程安全的替代方案(如
DateTimeFormatter) - 如果必须复用,加
synchronized锁住整个操作,但性能差,不推荐
pattern 字符大小写敏感,y/M/d/H/h/m/s 不能写错
比如年份写成 "YYYY" 和 "yyyy" 行为完全不同:YYYY 是“基于周的年”,可能跨年;yyyy 才是日历年。月份 "MM"(数字)和 "MMM"(缩写,如 Jan)也容易混淆。
-
"HH"是 24 小时制(00–23),"hh"是 12 小时制(01–12),漏掉一个字母就出错 - 分钟永远是
"mm"(小写),大写"MM"是月份 —— 这个错配导致解析失败很常见 - 秒是
"ss",毫秒是"SSS"(三个 S),少一个 S 会截断或补零
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String timeStr = sdf.format(new Date()); // 输出类似 "2024-06-15 14:23:05"
parse() 可能抛异常,别忘了 try-catch,且注意 lenient 模式
SimpleDateFormat.parse() 默认开启宽松解析(lenient = true),比如把 "2024-13-01" 自动转成 2025 年 1 月 1 日,掩盖真实输入错误。
- 生产环境建议显式关闭:
sdf.setLenient(false),这样非法日期直接抛ParseException - 必须捕获
ParseException,不能只 catchException—— 否则 IDE 可能提示未处理受检异常 - 输入字符串含时区(如
"2024-06-15T14:23:05+0800")而 pattern 没写时区部分("Z"或"X"),会解析失败
替代方案优先选 DateTimeFormatter(Java 8+)
除非维护老项目,否则不要在新代码里用 SimpleDateFormat。它设计陈旧、不可变性差、API 不直观。
立即学习“Java免费学习笔记(深入)”;
-
DateTimeFormatter是不可变、线程安全的,可直接静态复用 - 支持 ISO 标准格式开箱即用,例如
DateTimeFormatter.ISO_LOCAL_DATE_TIME - 时区处理更清晰:
ZonedDateTime+withZoneSameInstant()比SimpleDateFormat.setTimeZone()少出错
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
String formatted = LocalDateTime.now().format(formatter);
LocalDateTime parsed = LocalDateTime.parse("2024-06-15 14:23:05", formatter);
时区、模式字符串大小写、线程安全这三点,只要漏掉一个,线上就容易出难以复现的时间错乱问题。










