Java计算项目及任务时长始终返回0问题排查求助
排查项目时长计算始终返回0的问题
以下是几个可能的原因及对应的解决办法:
1. 毫秒转天数的精度问题
TimeUnit.DAYS.convert是向下取整计算,只要两个日期的毫秒差不足24小时(86400000毫秒),哪怕差23小时59分,结果都会是0天。比如任务开始于当天0点,结束于当天23点,计算后返回0。
解决办法:
改用按自然日期字段计算差值,而非纯毫秒数转换。推荐使用Java 8+的LocalDate(替代过时的Date/Calendar):
public static int computeDuration(Date from, Date to) { // 指定时区,避免时区偏差,比如ZoneId.of("Asia/Shanghai") ZoneId zone = ZoneId.systemDefault(); LocalDate localFrom = from.toInstant().atZone(zone).toLocalDate(); LocalDate localTo = to.toInstant().atZone(zone).toLocalDate(); return Math.abs((int) ChronoUnit.DAYS.between(localFrom, localTo)); }
如果仍需使用Calendar,可以重置时间部分后再计算:
public static int computeDuration(Date from, Date to) { Calendar calFrom = Calendar.getInstance(); calFrom.setTime(from); Calendar calTo = Calendar.getInstance(); calTo.setTime(to); // 清空时间字段,只保留年月日 calFrom.set(Calendar.HOUR_OF_DAY, 0); calFrom.set(Calendar.MINUTE, 0); calFrom.set(Calendar.SECOND, 0); calFrom.set(Calendar.MILLISECOND, 0); calTo.set(Calendar.HOUR_OF_DAY, 0); calTo.set(Calendar.MINUTE, 0); calTo.set(Calendar.SECOND, 0); calTo.set(Calendar.MILLISECOND, 0); long diffInMillis = Math.abs(calTo.getTimeInMillis() - calFrom.getTimeInMillis()); return (int) TimeUnit.DAYS.convert(diffInMillis, TimeUnit.MILLISECONDS); }
2. 初始minDate/maxDate的风险
当前代码直接用projectTasks[0]初始化minDate和maxDate,如果第一个任务的开始/结束日期相同,或者后续没有任务的日期超出这个范围,就会导致minDate和maxDate相等,计算出的时长为0。另外,如果projectTasks[0]为空或日期字段为空,还会引发空指针异常。
解决办法:
先遍历所有任务,动态初始化minDate和maxDate:
Date minDate = null; Date maxDate = null; // 第一步:遍历确定项目的最早开始和最晚结束日期 for (int i = 0; i < numOfTasks; ++i) { if (projectTasks[i] != null && projectTasks[i].gettaskEndDate() != null && projectTasks[i].gettaskFromDate() != null) { Date taskStart = projectTasks[i].gettaskFromDate(); Date taskEnd = projectTasks[i].gettaskEndDate(); if (minDate == null || taskStart.before(minDate)) { minDate = taskStart; } if (maxDate == null || maxDate.before(taskEnd)) { maxDate = taskEnd; } } } // 第二步:计算每个任务的时长 for (int i = 0; i < numOfTasks; ++i) { if (projectTasks[i] != null && projectTasks[i].gettaskEndDate() != null && projectTasks[i].gettaskFromDate() != null) { projectTasks[i].settaskDuration( utils.Globals.computeDuration( projectTasks[i].gettaskFromDate(), projectTasks[i].gettaskEndDate() ) ); } } // 第三步:计算项目总时长(处理无有效任务的情况) if (minDate != null && maxDate != null) { setprojectDuration(utils.Globals.computeDuration(minDate, maxDate)); } else { setprojectDuration(0); // 或根据业务需求抛出异常 }
3. 日期对象的实际值异常
如果任务的开始/结束日期被设置为同一个时间点(毫秒数相同),或者所有任务的日期范围没有跨度,计算结果自然是0。
排查办法:
在computeDuration方法中添加日志,打印日期的毫秒值和差值,确认数据是否正常:
public static int computeDuration(Date from, Date to){ long fromMillis = from.getTime(); long toMillis = to.getTime(); long diffIn = Math.abs(toMillis - fromMillis); System.out.printf("From: %s (%dms), To: %s (%dms), 差值: %dms%n", from, fromMillis, to, toMillis, diffIn); int duration = (int) TimeUnit.DAYS.convert(diffIn, TimeUnit.MILLISECONDS); System.out.println("计算出的时长: " + duration); return duration; }
4. 冗余的validateDates方法
当前validateDates方法只是封装了from.before(to),直接在判断中使用原方法即可,减少不必要的方法调用,逻辑更直观。
内容的提问来源于stack exchange,提问作者dannyph
相关产品推荐
相关产品推荐

