在Java中SimpleDateFormat如何格式化时间_Java日期格式化解析

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()); // 输出类似 "2025-06-15 14:23:05"

parse() 可能抛异常,别忘了 try-catch,且注意 lenient 模式

SimpleDateFormat.parse() 默认开启宽松解析(lenient = true),比如把 "2025-13-01" 自动转成 2025 年 1 月 1 日,掩盖真实输入错误。

  • 生产环境建议显式关闭:sdf.setLenient(false),这样非法日期直接抛 ParseException
  • 必须捕获 ParseException,不能只 catch Exception —— 否则 IDE 可能提示未处理受检异常
  • 输入字符串含时区(如 "2025-06-15T14:23:05+0800")而 pattern 没写时区部分("Z""X"),会解析失败

替代方案优先选 DateTimeFormatter(Java 8+)

除非维护老项目,否则不要在新代码里用 SimpleDateFormat。它设计陈旧、不可变性差、API 不直观。

  • 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("2025-06-15 14:23:05", formatter);
时区、模式字符串大小写、线程安全这三点,只要漏掉一个,线上就容易出难以复现的时间错乱问题。