多个日期时间段进行合并,算出经历的时长_itrytellyou的博客-CSDN博客_多个时间段合并多个日期时间段进行合并,算出经历的时长背景描述思路代码背景描述在公司进行人员描图模块开发时,涉及到了人员参会时长和参会次数的数据,比如一个人,两个月内开了14次会议。这里面就涉及到了,如果这个人参加的两个甚至多个会议有重叠的部分,这样的时间如何通过程序去进行合并处理。思路我们在程序中,可以将多个时间段中的两段分别“冒泡”比较,如果有重叠,那么进行合并,将这两个段去除,将合并后的时间段加入,...https://blog.csdn.net/itrytellyou/article/details/102833530 最近项目上遇到需要计算有效时长的算法问题,在多个时间段中,剔除重叠的时间段计算出时长。看了上面大佬的文章后深受启发,为了应对大量的时间参与递归冒泡比较时可能出现Stack Overflow问题,我从不同方向去实现功能。
直接上代码:
import com.houhou.emmplatform.constant.DateFormatConstant;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.Instant;
import java.time.temporal.ChronoUnit;
import java.util.*;
public class TimeNode {
//时间的long值
private final long time;
//开始时间1,结束时间-1
private final long direct;
//累积和
private long sum = 0;
public TimeNode(Date dateTime, long direct) {
this.time = dateTime.getTime();
this.direct = direct;
}
public long getDirect() {
return direct;
}
public long getTime() {
return time;
}
public long getSum() {
return sum;
}
public void setSum(long sum) {
this.sum = sum;
}
//合并有重叠区别的时间
public static List<TimeNode> merge(List<TimeBucket> timeBucketList) {
List<TimeNode> nodeList = new ArrayList<>(2 * timeBucketList.size());
for (TimeBucket timeBucket : timeBucketList) {
nodeList.add(new TimeNode(timeBucket.getStart(), 1));
nodeList.add(new TimeNode(timeBucket.getEnd(), -1));
}
nodeList.sort(Comparator.comparing(TimeNode::getTime));
//设置第一个节点的和
TimeNode firstNode = nodeList.get(0);
firstNode.setSum(firstNode.getDirect());
//计算后续节点的累计和(上一个节点的和与当前节点的方向之和)
for (int i = 1; i < nodeList.size(); i++) {
TimeNode preNode = nodeList.get(i - 1);
TimeNode currentNode = nodeList.get(i);
currentNode.setSum(preNode.getSum() + currentNode.getDirect());
}
//只保留 direct=1 sum=1的开始节点 和 direct=1 sum=0的结束节点
nodeList.removeIf(n -> !((n.getDirect() == 1 && n.getSum() == 1) || (n.getDirect() == -1 && n.getSum() == 0)));
return nodeList;
}
//将 List<TimeNode> 转换成 List<TimeBucket> 类型
public static List<TimeBucket> convert(List<TimeNode> nodeList) {
List<TimeBucket> timeBucketList = new ArrayList<>(nodeList.size() / 2);
for (int i = 0; i < nodeList.size(); i = i + 2) {
long start = nodeList.get(i).getTime();
long end = nodeList.get(i + 1).getTime();
timeBucketList.add(new TimeBucket(new Date(start), new Date(end)));
}
return timeBucketList;
}
public static void main(String[] args) throws ParseException {
List<TimeBucket> timeBucketList = new ArrayList<>();
SimpleDateFormat sdf = new SimpleDateFormat(DateFormatConstant.SECOND_OF_YEAR);
// timeBucketList.add(new TimeBucket(sdf.parse("2022-03-14 19:00:00"), sdf.parse("2022-03-15 05:00:00")));
// timeBucketList.add(new TimeBucket(sdf.parse("2022-03-13 18:00:00"), sdf.parse("2022-03-14 05:00:00")));
// timeBucketList.add(new TimeBucket(sdf.parse("2022-03-15 01:00:00"), sdf.parse("2022-03-15 03:00:00")));
// timeBucketList.add(new TimeBucket(sdf.parse("2022-03-13 08:00:00"), sdf.parse("2022-03-13 12:00:00")));
// timeBucketList.add(new TimeBucket(sdf.parse("2022-03-13 10:00:00"), sdf.parse("2022-03-14 02:00:00")));
// timeBucketList.add(new TimeBucket(sdf.parse("2022-03-14 07:00:00"), sdf.parse("2022-03-14 13:00:00")));
for (int i = 0; i < 10000; i++) {
Random tsdf = new Random();
Instant now = Instant.now();
Instant plus = now.plus(tsdf.nextInt(10), ChronoUnit.DAYS);
timeBucketList.add(new TimeBucket(new Date(plus.toEpochMilli()), new Date(plus.plus(tsdf.nextInt(10), ChronoUnit.MINUTES).toEpochMilli())));
}
long l = System.currentTimeMillis();
System.out.println(l);
List<TimeNode> nodeList = merge(timeBucketList);
System.out.println((System.currentTimeMillis() - l));
List<TimeBucket> timeBuckets = convert(nodeList);
for (TimeBucket timeBucket : timeBuckets) {
System.out.println("[" + sdf.format(timeBucket.getStart()) + "," + timeBucket.getEnd() + "]");
}
System.out.println("--------------------");
for (TimeNode timeNode : nodeList) {
System.out.println("[时间=" + sdf.format(timeNode.getTime()) + ",方向=" + timeNode.getDirect() + ",和=" + timeNode.getSum() + "]");
}
}
}
import java.util.*;
public class TimeBucket {
private final Date start;
private final Date end;
public TimeBucket(Date start, Date end) {
if (start.after(end)) {
throw new IllegalArgumentException("时间段无效(开始日期需要小于结束日期)");
}
this.start = start;
this.end = end;
}
public Date getStart() {
return start;
}
public Date getEnd() {
return end;
}
}
思路就是用 TimeBucket 类,TimeBucket 作为中间类,可以可不用。为了方便测试,我这里就用TimeBucket 集合来批量接收开始时间和结束时间,将TimeBucket放入TimeNode,这里只存放时间的long值,是开始时间方向direct就设置为1,结束时间就为-1。
循环遍历TimeNode,求当前节点的direct与前一个TimeNode的sum的值,设置到当前节点的sum。可以发现最后 direct=1 sum=1的开始节点 和 direct=1 sum=0的结束节点 是我们最终需要的节点。后面就能很容易计算出我们的需要的总时长了。