You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Java Calendar返回错误日期:同月起止日期校验异常求助

问题分析与解决方案:Calendar日期处理错误导致跨月校验失败

核心问题定位

你代码里有一个关键笔误,直接导致整个逻辑彻底混乱:

Calendar cend = Calendar.getInstance();
cstart.setTime(endInterval); // 此处错误!应该是cend.setTime(endInterval)

你创建了cend实例,但根本没给它设置结束日期,反而把已经设置好起始时间的cstart重新覆盖成了结束时间。这就引发了一系列连锁问题:

  • cstart实际存储的是结束日期,而非你期望的起始日期
  • cend始终是当前系统时间(比如日志里的2018年1月23日)
  • 后续的月份校验cstart.get(Calendar.MONTH) != cend.get(Calendar.MONTH)必然为true——一个是12月(对应Calendar的11,因为它是0-based月份),一个是1月(对应Calendar的0),所以肯定会抛出异常。

另外还要注意:Calendar的月份是0-based的,也就是1月对应0,12月对应11,这也是日志里31.11.2017实际代表2017年12月31日的原因,这个特性很容易引发误解。

修复后的Calendar版本代码

先修正笔误,再补充年份判断避免跨年同月份的情况:

Calendar cstart = Calendar.getInstance();
cstart.setTime(startInterval);
Calendar cend = Calendar.getInstance();
cend.setTime(endInterval); // 修正此处的笔误

int daysInMonth = cstart.getActualMaximum(Calendar.DAY_OF_MONTH);
// 补充年份判断,避免跨年的同月份被误判
if (cstart.get(Calendar.MONTH) != cend.get(Calendar.MONTH) 
    || cstart.get(Calendar.YEAR) != cend.get(Calendar.YEAR)) {
    throw new FacadeException(AccountingFacadeMessages.BILLING_NOT_WITHIN_MONTH);
}

LOG.info("EndInterval " + endInterval.toString() );
LOG.info("StartInterval " + startInterval.toString() );
// 把0-based月份转成1-based,方便阅读日志
LOG.info(cstart.get(Calendar.DATE) + "." + (cstart.get(Calendar.MONTH)+1) + "." + cstart.get(Calendar.YEAR));
LOG.info("Days: " + daysInMonth + "\n");

更推荐的解决方案:使用Java 8+的java.time API

Calendar类设计老旧,坑点太多(比如0-based月份、可变实例导致的意外修改),建议换成Java 8引入的java.time包,它的设计更直观、线程安全,完全避开了Calendar的各种问题:

import java.time.LocalDate;
import java.time.ZoneId;

// 如果你的startInterval/endInterval是Date类型,转成LocalDate
LocalDate start = startInterval.toInstant().atZone(ZoneId.systemDefault()).toLocalDate();
LocalDate end = endInterval.toInstant().atZone(ZoneId.systemDefault()).toLocalDate();

int daysInMonth = start.lengthOfMonth();
// 直接判断年月是否相同,月份是1-based,完全符合直觉
if (!start.getYear().equals(end.getYear()) || !start.getMonth().equals(end.getMonth())) {
    throw new FacadeException(AccountingFacadeMessages.BILLING_NOT_WITHIN_MONTH);
}

LOG.info("EndInterval " + end.toString() );
LOG.info("StartInterval " + start.toString() );
LOG.info(start.getDayOfMonth() + "." + start.getMonthValue() + "." + start.getYear());
LOG.info("Days: " + daysInMonth + "\n");

这个版本代码可读性和可靠性都更高,后续维护也更省心。

内容的提问来源于stack exchange,提问作者Lorayne

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.15 04:07:12