
SimpleDateFormat 线程不安全,别在多线程里复用同一个实例
直接把 SimpleDateFormat 声明为 static 变量,在并发场景下(比如 Spring 的 service 方法里反复调用)会导致格式化结果错乱,甚至抛出 java.lang.ArrayIndexOutOfBoundsException 或返回错误时间。这不是偶发 bug,是类设计上就未同步内部状态(如 calendar 字段)。
实操建议:
- 每次使用都新建实例:开销不大,比修复线程问题成本低得多
- 用
ThreadLocal缓存,但要注意内存泄漏风险(尤其在 Tomcat 等容器中未清理) - Java 8+ 强烈推荐换成
DateTimeFormatter(不可变、线程安全),例如:DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")
pattern 字符大小写敏感,常见误写:大写 YYYY 和小写 yyyy 不等价
YYYY 是「基于周的年份」(week year),按 ISO 8601 规则计算,可能和日历年份不同。比如 2024-12-30 是周一,它属于 2025 年第 1 周,用 YYYY 会输出 2025;而 yyyy 才是常规日历年份,输出 2024。
其他易错点:
立即学习“Java免费学习笔记(深入)”;
-
mm是分钟(minute),MM才是月份(month) -
hh是 12 小时制小时(1–12),HH是 24 小时制(0–23) -
SSS是毫秒(0–999),不是秒;秒是ss
parse() 方法默认宽松解析,容易掩盖输入错误
默认情况下,SimpleDateFormat 的 lenient 模式为 true,意味着它会自动“纠正”非法日期。例如:parse("2024-13-01") 不报错,而是转成 2025-01-01(13 月 → 下一年 1 月);parse("2024-02-30") 也可能变成 2024-03-01。
要严格校验,必须显式关闭:
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
sdf.setLenient(false);
sdf.parse("2024-02-30"); // 这里会抛 ParseException
时区和本地化行为隐含影响格式化结果
SimpleDateFormat 默认使用 JVM 启动时的默认时区(TimeZone.getDefault())和默认 Locale。同一段代码在不同时区服务器上运行,可能输出不同结果,尤其是涉及 AM/PM、星期几、月份名称时。
确保行为一致的做法:
- 显式设置时区:
sdf.setTimeZone(TimeZone.getTimeZone("GMT+8")) - 显式指定语言环境:
new SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.US) - 避免依赖默认值,特别是做跨系统数据交换或日志记录时










