y表示年份,y表示周所属年份(如2024年12月30日属2025年第1周,y=2024、y=2025);m为月份、m为分钟;h(0–23)、k(1–24)、h(1–12)、k(0–11)代表不同小时制;simpledateformat线程不安全,需每次新建或用threadlocal。

SimpleDateFormat里y和Y的区别,一错就跨年
用SimpleDateFormat格式化日期时,y是“年份”,Y是“周所属的年份”(week year),不是同一个概念。很多人在处理12月底或1月初的日期时发现格式化结果年份错了一年,基本就是混用了y和Y。
比如2024年12月30日是周一,属于2025年第1周(ISO 8601规则),此时Y会输出2025,而y仍是2024。只有当你明确需要按“周历”对齐年份(例如报表按周统计、财务周期)才该用Y。
- 日常显示年份、存数据库、做时间范围判断,一律用
y -
Y只在配合w(年中第几周)或u(周内第几天)使用时有意义 - JDK 7+ 默认使用ISO 8601周规则,
Y行为与系统区域设置无关,但getWeekYear()可能受Calendar实例影响
M和m大小写搞反,分钟变月份
M代表月份(1–12),m代表分钟(0–59)。这是SimpleDateFormat里最常打错的大小写之一,尤其在拼写yyyy-MM-dd HH:mm:ss这种常见模式时,手快把第二个m写成M,结果分钟字段变成月份——比如原本14:05被格式成14:05(显示为14:05没错),但解析"2024-01-01 14:05:00"时若模式是HH:MM:ss,就会把05当成“5月”,抛出IllegalArgumentException: Cannot parse "05" as month。
- 记住口诀:“Month 大写,Minute 小写”
- 模式字符串里连续两个
M(如MM)表示补零月份;MMM是缩写(Jan),MMMM是全称(January) - 编译器不报错,运行时才暴露,建议所有日期模式字符串加单元测试覆盖边界值(如1月、12月、0分、59分)
H、k、K、h这四个“小时”符号怎么选
Java里小时有四套定义,对应不同计时制和起始点:H是24小时制(0–23),k也是24小时制但范围是1–24,h是12小时制(1–12),K是12小时制但范围是0–11。用错一个,午夜和正午就乱套。
立即学习“Java免费学习笔记(深入)”;
例如00:30(凌晨0:30):用HH:mm得00:30,用kk:mm得24:30(错误),用hh:mm a得12:30 AM,用KK:mm a得00:30 AM(合法但非常规)。
- Web API返回/接收ISO格式时间(如
2024-01-01T15:30:00)统一用HH:mm:ss - 面向用户的中文界面显示“下午3:30”,用
hh:mm a+Locale.CHINA -
k和K极少用,除非对接老系统要求“24点计时法从1开始”(如某些排班表) - 注意
SimpleDateFormat默认不校验小时合理性——parse("25:00", "HH:mm")可能返回奇怪日期,需手动检查
线程不安全是硬伤,别在工具类里直接new SimpleDateFormat
SimpleDateFormat不是线程安全的。多个线程共用同一个实例做format或parse,大概率出现格式错乱、抛NumberFormatException、甚至死循环。这不是偶发bug,是内部状态(如calendar字段)被并发修改导致的确定性问题。
- 最稳妥:每次用都
new SimpleDateFormat("yyyy-MM-dd")(JDK 8+推荐用DateTimeFormatter替代) - 次选:用
ThreadLocal<simpledateformat></simpledateformat>缓存,避免重复创建开销 - 绝对别写
public static final SimpleDateFormat SDF = new SimpleDateFormat("...") - 如果必须复用,确保调用方严格串行,且无任何异步、并行、定时任务场景
模式字母含义本身不复杂,真正卡住人的永远是:大小写敏感、线程共享、以及ISO周年的隐式规则。这些地方没踩过坑,不算真的用过SimpleDateFormat。










