java格式化时间应优先用线程安全的datetimeformatter;24小时制用"hh:mm:ss",12小时制需"hh:mm:ss a"并指定locale.us;注意mm/mm、hh/hh、dd/dd等大小写区分及simpledateformat非线程安全问题。

Java 用 SimpleDateFormat 格式化年月日时分秒(24 小时制)
24 小时制是默认且最常用的时间格式,关键在小时部分用 HH(00–23),不是 hh(01–12)。注意 SimpleDateFormat 非线程安全,多线程环境下必须每个线程独享实例或改用 DateTimeFormatter。
常见写法示例:
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
对应输出如:2024-06-15 14:30:22
-
yyyy:4 位年份(yy是 2 位,易歧义) -
MM:月份(mm是分钟,大小写敏感!) -
dd:日期(DD是当年第几天,别混) -
HH:24 小时制小时(00–23),H(无前导零)也可用但不推荐 -
mm:分钟(MM是月,务必区分) -
ss:秒(SSS才是毫秒)
Java 用 SimpleDateFormat 格式化 12 小时制(带 AM/PM)
12 小时制需同时满足两个条件:小时用 hh(01–12),并显式加上 a(AM/PM 标记),否则时间会错乱或抛 IllegalArgumentException。
立即学习“Java免费学习笔记(深入)”;
正确写法:
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss a");
输出示例:2024-06-15 02:30:22 PM
-
hh表示 12 小时制(01–12),HH与之互斥 -
a必须存在,否则hh会被当成 24 小时制解析(比如14会变成02,但没 AM/PM 就语义断裂) - 本地化影响
a的显示(中文环境可能输出“上午”“下午”,需配合Locale.US固定为 AM/PM) - 如果传入的
Date对象本身是 24 小时制时间(如14:30),hh+a会自动转成02:30 PM,无需手动换算
Java 8+ 推荐用 DateTimeFormatter 替代 SimpleDateFormat
SimpleDateFormat 已被标记为 legacy,新项目应优先使用不可变、线程安全的 DateTimeFormatter。它基于 LocalDateTime / ZonedDateTime,不依赖 Date 和 Calendar。
24 小时制示例:
DateTimeFormatter f = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");<br>String s = LocalDateTime.now().format(f);
12 小时制(固定英文 AM/PM):
DateTimeFormatter f = DateTimeFormatter.ofPattern("yyyy-MM-dd hh:mm:ss a", Locale.US);
-
DateTimeFormatter是 final 类,线程安全,可复用 - 模式字母含义与
SimpleDateFormat基本一致,但a在非 US locale 下可能不显示 AM/PM(比如中文 locale 显示“上午”“下午”,但某些 Android 版本有兼容问题) - 不能直接格式化
Date,需先转:Instant.ofEpochMilli(date.getTime()).atZone(ZoneId.systemDefault()).toLocalDateTime() - 若需毫秒,用
SSS;纳秒级精度需用nnnnnnnnn,但普通日志场景SSS足够
容易踩的坑:大小写、locale、线程安全三连击
这三个点加起来能覆盖 80% 的时间格式化报错和诡异输出。
-
MM(月)和mm(分)写反 → 时间变成“2024-30-15 14:06:22”这种非法值,解析时报ParseException - 用
hh却漏掉a→ 输出02:30:22看似正常,但语义缺失,且后续解析会失败(parse时找不到 AM/PM 报错) - 在 Spring Bean 中把
SimpleDateFormat声明为@Bean或 static 字段 → 多线程并发格式化时出现java.lang.NumberFormatException或乱码(如 “2024-06-15 9a:3b:2c”) - 未指定
Locale→ 英文系统下a显示 AM/PM,中文系统可能显示“上午”,但某些旧版 JRE 或 Android 会 fallback 到空字符串,导致格式崩溃
真正要稳定跑在线上,要么用 DateTimeFormatter + Locale.US,要么每次 new SimpleDateFormat 并设好 setLenient(false)。别省那点对象创建开销。











