如何在Java中计算同一任务下type=0的多段时间范围总时长并去除重叠时间
如何在Java中合并重叠时间段并计算总耗时?
我完全懂你的痛点——直接用Duration.between计算每段时间再累加,会把重叠部分重复计算,得先把重叠的时间段合并成不重叠的连续段,再算总时长就准确了。针对你给出的SQL数据,咱们一步步来实现:
核心思路
- 排序:先把所有
type=0的时间段按开始时间从小到大排序,这是合并操作的基础; - 合并重叠/连续段:遍历排序后的时间段,和已合并的最后一段对比,重叠或连续就更新结束时间,不重叠就新增一段;
- 计算总时长:对合并后的每段时间用
Duration.between计算时长,再累加得到总耗时。
代码实现
首先定义对应数据库表的实体类:
import java.time.LocalDateTime; public class Timeline { private LocalDateTime startTime; private LocalDateTime endTime; private int type; // 构造器 public Timeline(LocalDateTime startTime, LocalDateTime endTime, int type) { this.startTime = startTime; this.endTime = endTime; this.type = type; } // Getter和Setter public LocalDateTime getStartTime() { return startTime; } public void setStartTime(LocalDateTime startTime) { this.startTime = startTime; } public LocalDateTime getEndTime() { return endTime; } public void setEndTime(LocalDateTime endTime) { this.endTime = endTime; } public int getType() { return type; } public void setType(int type) { this.type = type; } }
然后写合并和计算的核心逻辑:
import java.time.Duration; import java.time.LocalDateTime; import java.util.ArrayList; import java.util.Comparator; import java.util.List; public class NonOverlappingTimeCalculator { public static Duration calculateTotalNonOverlappingTime(List<Timeline> type0Timelines) { // 第一步:按开始时间排序 type0Timelines.sort(Comparator.comparing(Timeline::getStartTime)); List<Timeline> mergedTimelines = new ArrayList<>(); for (Timeline current : type0Timelines) { if (mergedTimelines.isEmpty()) { // 第一个时间段直接加入合并列表 mergedTimelines.add(current); } else { Timeline lastMerged = mergedTimelines.get(mergedTimelines.size() - 1); // 判断当前时间段是否和最后一段重叠或连续 if (current.getStartTime().isBefore(lastMerged.getEndTime()) || current.getStartTime().isEqual(lastMerged.getEndTime())) { // 合并操作:更新最后一段的结束时间为两者的最大值 LocalDateTime newEnd = lastMerged.getEndTime().isAfter(current.getEndTime()) ? lastMerged.getEndTime() : current.getEndTime(); lastMerged.setEndTime(newEnd); } else { // 不重叠,直接加入合并列表 mergedTimelines.add(current); } } } // 第二步:计算合并后的总时长 Duration totalDuration = Duration.ZERO; for (Timeline timeline : mergedTimelines) { totalDuration = totalDuration.plus(Duration.between(timeline.getStartTime(), timeline.getEndTime())); } return totalDuration; } public static void main(String[] args) { // 模拟从数据库查询到的type=0数据 List<Timeline> type0Records = new ArrayList<>(); type0Records.add(new Timeline( LocalDateTime.parse("2022-07-29T15:30:03.412754"), LocalDateTime.parse("2022-07-29T15:57:03.965432"), 0 )); type0Records.add(new Timeline( LocalDateTime.parse("2022-07-29T15:40:03.414000"), LocalDateTime.parse("2022-07-29T16:32:03.004323"), 0 )); type0Records.add(new Timeline( LocalDateTime.parse("2022-07-29T16:50:03.643231"), LocalDateTime.parse("2022-07-29T17:35:03.234562"), 0 )); Duration total = calculateTotalNonOverlappingTime(type0Records); System.out.println("无重叠总耗时:" + total); // 输出结果对应合并后的两段时间总和:1小时2分59秒左右 } }
额外说明
- 如果不想合并连续的时间段(比如前一段的结束时间刚好等于后一段的开始时间),只需要把判断条件里的
|| current.getStartTime().isEqual(lastMerged.getEndTime())去掉即可; - 这个逻辑同样能处理包含关系的时间段(比如一个大时间段完全包含小时间段),会自动保留最长的结束时间;
- 实际项目中,你只需要把数据库查询到的
type=0数据转换成Timeline列表,传入方法即可。
内容的提问来源于stack exchange,提问作者Berkan Aslan
相关产品推荐
相关产品推荐

