在Java中如何使用SimpleDateFormat格式化日期_Java日期格式化说明

SimpleDateFormat线程不安全且模式敏感,应避免static复用;推荐Java 8+的DateTimeFormatter,它线程安全、不可变、语义清晰。

SimpleDateFormat 线程不安全,别在多线程里复用同一个实例

SimpleDateFormat 不是线程安全的。如果多个线程共用一个 SimpleDateFormat 实例(比如定义为 static 字段),极大概率出现格式化错乱、抛出 java.lang.ArrayIndexOutOfBoundsException 或返回错误日期字符串。

  • 每次使用都新建实例:开销小,最稳妥
  • ThreadLocal 封装:适合高频调用且需复用模式的场景
  • 避免 static SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd") 这类写法

模式字母大小写敏感,常见误写:小写 mm 当作分钟,实际是分钟,MM 才是月份

SimpleDateFormat 的模式字符串中,MM 表示月(01–12),mm 表示分(00–59)。把 "yyyy-mm-dd" 当作“年-月-日”用,结果会把当前分钟塞进“月”的位置,例如输出 "2025-42-15"(假设当前是 14:42)。

  • yyyy:4位年份
  • MM:两位月份(1月 → "01"
  • dd:两位日期
  • HH:24小时制小时(00–23)
  • hh:12小时制小时(01–12)
  • mm:分钟(00–59)
  • ss:秒(00–59)

解析字符串时,lenient 默认为 true,会导致奇怪的自动修正

默认情况下,SimpleDateFormatlenient 属性为 true,它会尝试“理解”非法日期,比如把 "2025-02-30" 解析成 2025 年 3 月 1 日(2月只有29天),而不是报错。

  • 业务上要求严格校验时,必须调用 sdf.setLenient(false)
  • 设为 false

    后,遇到无效日期(如 "2025-02-30""2025-13-01")会抛出 ParseException
  • 注意:格式化(date → string)不受 lenient 影响,只影响解析(string → date)

替代方案:优先考虑 DateTimeFormatter(Java 8+)

SimpleDateFormat 是遗留 API,设计缺陷多、线程不安全、容错逻辑难控。Java 8 引入的 java.time.format.DateTimeFormatter 是不可变、线程安全、语义清晰的现代替代品。

DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
LocalDateTime dt = LocalDateTime.now();
String text = dt.format(formatter); // "2025-06-15 14:23:45"

// 解析
LocalDateTime parsed = LocalDateTime.parse("2025-06-15 14:23:45", formatter);
  • DateTimeFormatter 实例可安全共享、复用、甚至设为 static final
  • 没有 lenient 开关,但可通过 ResolverStyle.STRICT 显式控制解析严格性
  • LocalDateTimeZonedDateTime 等新时间类型配合自然,避免老 DateCalendar 的隐式时区陷阱
真正要兼容旧系统或维护老代码时才用 SimpleDateFormat;新项目直接上 DateTimeFormatter,否则迟早被线程问题和解析偏差咬一口。