正确闰年判断逻辑是(year % 4 == 0 && year % 100 != 0) || (year % 400 == 0),需避开世纪年份仅%4的常见错误;基准日应选0001-01-01而非1970-01-01以支持万年历;动态获取月天数优于硬编码;用toEpochDay()数学偏移替代循环累加提升性能;须处理1582年儒略历到格里高利历跳变。

闰年判断别只用 % 4 == 0
Java里写 year % 4 == 0 判断闰年,上线后2100年会出错——它漏掉了世纪年份必须被400整除的规则。真实业务中,万年历要撑过几百年,不能靠“大概率正确”。
- 正确逻辑是:
(year % 4 == 0 && year % 100 != 0) || (year % 400 == 0) - 别封装成单行三元表达式,可读性差还容易括号错位;直接写 if-return 更稳妥
- 注意
LocalDate.isLeapYear()是 JDK8+ 安全解法,但若项目还在用 JDK7 或需手动算天数偏移,就得自己实现
基准日期选 1970-01-01 还是 0001-01-01?
很多教程默认用 Unix 时间戳起点(1970-01-01)当基准,但万年历要查公元前或22世纪,这个起点太靠后,会导致大负数计算溢出或逻辑绕弯。
- 推荐用
0001-01-01(ISO日历第1天)作基准:所有年份统一处理,无符号偏移量更直观 - JDK8 的
LocalDate.toEpochDay()内部就以 0001-01-01 为原点,返回 long 值(如 1970-01-01 对应 719163),直接复用最省事 - 如果手写算法,得额外处理儒略历→格里高利历切换(1582年10月,跳10天),否则1500–1600年之间日期会偏1天
getDaysFromBase() 函数怎么避开月份天数硬编码
把12个月天数写死在数组里看似简单,但遇到闰年2月、跨世纪历法变更时,硬编码会变成bug温床。
- 用
YearMonth.of(year, month).lengthOfMonth()动态获取天数,自动适配闰年,JDK8+ - 如果必须手算:先累计前
month - 1月天数,再加当月日数;其中2月天数由前述闰年函数决定 - 避免用
Calendar.getActualMaximum(Calendar.DAY_OF_MONTH):它依赖系统默认时区和Locale,测试环境和生产环境可能不一致
查询性能卡在循环逐日累加?
有人为算某天是星期几,从基准日开始 while 循环加一天直到目标日——查2050年某天要循环上万次,响应直接超时。
立即学习“Java免费学习笔记(深入)”;
- 改用数学偏移:目标日
toEpochDay() - baseDay得总天数差,再% 7就是星期偏移,O(1) - 注意
toEpochDay()返回值范围是 -719162(0001-01-01)到 +719162(9999-12-31),long 完全够用 - 如果输入是字符串如
"2025-03-12",先用LocalDate.parse()解析,别自己切分拼接,避免时区和格式歧义
闰年规则、基准选择、动态天数、数学偏移——这四点串起来才是稳的万年历底子。最容易被忽略的是历法断层:1582年10月4日后直接跳到10月15日,这段空档期如果没特殊标记,用户查10月10日会返回 null 或错误日期。










