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
相关产品推荐
相关产品推荐

