YearMonth 是处理信用卡过期时间的理想选择,因其不可变、线程安全,支持精准解析(如“03/25”)、校验(isBefore(YearMonth.now()))和格式化,避免 LocalDate 或字符串带来的日期边界问题。

Java 中 YearMonth 是处理信用卡过期时间这类“仅需年月、无需具体日期”的场景的理想选择。它不可变、线程安全,且天然支持比较、解析、格式化等操作,避免了用 LocalDate 或字符串硬凑带来的边界问题(比如误把 2025-03 当成 2025-03-01 去比,或遇到 2 月 31 日报错)。
用 YearMonth 表示并验证过期时间
信用卡有效期通常只印“MM/YY”或“MM/YYYY”,不涉及日。直接用 YearMonth 存储最准确:
- 解析用户输入(如 "03/25" 或 "03/2025")时,用
DateTimeFormatter定制格式,避免歧义 - 校验是否过期:用
isBefore(YearMonth.now())即可——注意是 before,不是 isAfter,因为过期意味着“该月已过去” - 不需要手动补 01 日再转
LocalDate,也无需担心 2 月 30 日等非法日期
安全解析不同格式的信用卡有效期
用户可能输 "03/25"、"03/2025"、"2025-03",甚至带空格。推荐统一转为 YearMonth:
- 定义多个
DateTimeFormatter(如MM/yy、MM/yyyy、yyyy-MM),按顺序尝试解析 - 对两位年份(如 "25"),按行业惯例视为 2025 年(即
year + (year < 50 ? 2000 : 1900)),但更稳妥的是明确要求前端传四位年份 - 解析失败时抛出业务异常(如
InvalidCardExpiryException),而不是静默处理
与当前时间比较的注意事项
YearMonth.now() 默认使用系统默认时区,而信用卡过期判断通常不依赖时区(全球统一按卡面月份失效)。但要注意:
立即学习“Java免费学习笔记(深入)”;
- 如果系统跨时区部署,建议显式指定时区,例如
YearMonth.now(ZoneId.of("UTC")),保持一致性 - 判断逻辑应为:
cardExpiry.isBefore(YearMonth.now(ZoneId.UTC))→ 过期;等于或之后 → 有效 - 不要用
cardExpiry.compareTo(YearMonth.now()) < 0,虽然结果一致,但语义不如isBefore清晰
序列化和存储建议
存数据库或传 JSON 时,别存原始对象(易版本兼容问题),推荐:
- 数据库:用
CHAR(7)存 "2025-03" 格式,或两个SMALLINT字段(year / month) - JSON:用
@JsonFormat(pattern = "yyyy-MM")注解,确保前后端约定统一 - 避免存为毫秒数或复杂嵌套对象——过期时间本质就是年+月,过度设计反而增加出错概率
不复杂但容易忽略。用对 YearMonth,信用卡过期校验就既健壮又干净。



















