yearmonth 是 java 8 引入的不可变类,专用于仅需年月的场景,如计费、账单等;支持创建、首末日获取、跨月计算及区间判断,语义清晰且线程安全。

YearMonth 是 Java 8 引入的 java.time 包中的不可变类,专为「只关心年份和月份」的业务场景设计,比如订阅计费、账单周期、合同期限、报表归档等。它不涉及日、时、分、秒,天然规避了日期边界问题(如 2 月 30 日),也比用 LocalDate 或字符串拼接更语义清晰、线程安全、易于计算。
表示固定年月并做基础操作
创建 YearMonth 很直接:
- 用静态工厂方法:
YearMonth.of(2024, 12)或YearMonth.parse("2024-12") - 从 LocalDate 提取:
LocalDate.now().withDayOfMonth(1).atStartOfDay().toLocalDate().getYearMonth()→ 更推荐YearMonth.from(localDate) - 获取首日/末日:调用
atDay(1)得到当月第一天的LocalDate;lengthOfMonth()返回天数(自动处理闰年);atEndOfMonth()直接得月末日期
跨月计算与周期管理
YearMonth 支持加减月数,适合周期性逻辑:
-
yearMonth.plusMonths(1)→ 下个月;minusMonths(3)→ 三个月前 - 计算两个 YearMonth 间隔月数:
long months = ChronoUnit.MONTHS.between(start, end)(注意:结果是整月差,不四舍五入) - 判断是否在某个区间内:
if (current.compareTo(start) >= 0 && current.compareTo(end) ,或封装成 <code>isBetweenInclusive(start, end)
与数据库和业务字段对齐
常见做法是数据库存 CHAR(7)(如 "2024-12")或 INT(如 202412),Java 层统一用 YearMonth 转换:
- 存库前:
ym.toString()→ "2024-12";或ym.getYear() * 100 + ym.getMonthValue()→ 202412 - 查库后:若为字符串,
YearMonth.parse(str);若为整数,YearMonth.of(num / 100, num % 100) - JPA 中可配合
@Convert自定义转换器,让实体字段直接声明为YearMonth
处理月末特殊逻辑(如账单日)
很多业务要求“每月最后一天扣费”,YearMonth 配合 atEndOfMonth() 可靠又简洁:
-
LocalDate billingDate = yearMonth.atEndOfMonth();→ 自动适配 28/29/30/31 - 避免手写
if (month == 2) ...判断,也不用依赖Calendar的复杂 API - 若需“每月第 N 天”,可用
atDay(Math.min(n, yearMonth.lengthOfMonth()))防越界
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











