时间段处理工具类(交并差集)

时间段由开始时间和结束时间组成,本工具类可以对时间段列表进行取并集,取交集,取差集,也可以返回单个时间点位于时间段列表中的位置。时间段的开始结束时间放在Date[]里 Date[0]表示开始时间,Date[1]表示结束时间。

import cn.hutool.core.date.DateUtil;

import java.text.ParseException;
import java.util.*;

/**
 * 时间段处理工具类
 * 用于进行时间段的合并(加法)/时间段的剔除(减法)/时间段的取交集,及单个时间点位于时间段内的位置。
 */
public class TimePeriodUtils {
    /**
     * 合并时间段集合
     *
     * @param timePeriods 时间段集合
     * @return 合并后的时间段集合
     */
    private static List<Date[]> mergeTimePeriods(List<Date[]> timePeriods) {
        if (timePeriods == null || timePeriods.isEmpty()) {
            return new ArrayList<>();
        }
        List<Date[]> resultList = new ArrayList<>();
        // 对时间段集合按开始时间排序
        Collections.sort(timePeriods, Comparator.comparing(Date -> Date[0]));
        Date[] timePeriod = timePeriods.get(0);
        for (int i = 1; i < timePeriods.size(); i++) {
            Date[] nextTimePeriod = timePeriods.get(i);
            if (nextTimePeriod[0].compareTo(timePeriod[1]) <= 0) {
                // 有交集,合并时间段
                timePeriod[1] = nextTimePeriod[1].compareTo(timePeriod[1]) > 0 ? nextTimePeriod[1] : timePeriod[1];
            } else {
                // 无交集,加入结果集
                resultList.add(timePeriod);
                timePeriod = nextTimePeriod;
            }
        }
        resultList.add(timePeriod);
        return resultList;
    }


    /**
     * 该函数接受两个参数:timePeriods和deleteTimePeriods。它遍历timePeriods中的每个时间段,
     * 对于每个时间段,检查它是否与任何deleteTimePeriods时间段重叠。如果重叠,它将时间段拆分为两部分,
     * 并将不重叠的部分添加到resultList中。
     * 最后,它返回resultList,其中包含减去deleteTimePeriods时间段后的所有时间段。
     *
     * @param timePeriods
     * @param deleteTimePeriods
     * @return
     */
    public static List<Date[]> subtractTimePeriods(List<Date[]> timePeriods, List<Date[]> deleteTimePeriods) {
        // 创建一个列表来存储timePeriods和deleteTimePeriods之间的差异
        ArrayList<Date[]> resultList = new ArrayList<>();
        for (Date[] dateRange : timePeriods) {
            boolean isOverlap = false;
            for (Date[] deleteRange : deleteTimePeriods) {
                if (dateRange[0].before(deleteRange[1]) && deleteRange[0].before(dateRange[1])) {
                    // 如果两个时间段重叠,则将时间段拆分为两个部分
                    if (dateRange[0].before(deleteRange[0])) {
                        resultList.add(new Date[]{dateRange[0], deleteRange[0]});
                    }
                    if (dateRange[1].after(deleteRange[1])) {
                        resultList.add(new Date[]{deleteRange[1], dateRange[1]});
                    }
                    isOverlap = true;
                    break;
                }
            }
            if (!isOverlap) {
                // 如果时间段与任何deleteTimePeriods时间段不重叠,则将其添加到resultList中
                resultList.add(dateRange);
            }
        }

        return resultList;
    }

    /**
     * 该函数接受两个参数:timePeriods和datePoint。它遍历timePeriods中的每个时间段,
     * 如果datePoint在该时间段内,则返回该时间段的开始和结束时间以及结果类型1。
     * 如果datePoint不在任何时间段内,则它将遍历时间段以找到在datePoint时间点之前的最大结束时间和在datePoint时间点之后的最小开始时间,并返回这些时间以及结果类型2。
     * 如果timePeriods列表为空,则它将返回null和结果类型-1。
     *
     * @param timePeriods 时间段集合
     * @param datePoint   时间点 该时间点需要加个一秒钟以避免与端点重合引起bug
     * @return 位置信息数组
     */
    public static Object[] getTimePeriodPosition(List<Date[]> timePeriods, Date datePoint) {
        for (Date[] timePeriod : timePeriods) {
            // 如果datePoint在timePeriod范围内,则返回开始和结束时间以及结果类型1
            if (timePeriod[0].before(datePoint) && timePeriod[1].after(datePoint)) {
                return new Object[]{1, timePeriod[0], timePeriod[1]};
            }
        }

        Date maxEndTime = null;
        Date minStartTime = null;
        for (Date[] timePeriod : timePeriods) {
            // 如果datePoint在timePeriod之前,则更新最大结束时间
            if (timePeriod[1].before(datePoint) && (maxEndTime == null || timePeriod[1].after(maxEndTime))) {
                maxEndTime = timePeriod[1];
            }
            // 如果datePoint在timePeriod之后,则更新最小开始时间
            else if (timePeriod[0].after(datePoint) && (minStartTime == null || timePeriod[0].before(minStartTime))) {
                minStartTime = timePeriod[0];
            }
        }

        // 如果最大结束时间和最小开始时间都为空,则说明timePeriods列表为空
        if (maxEndTime == null && minStartTime == null) {
            return new Object[]{-1, null, null};
        }

        // 如果最大结束时间为空,则说明datePoint在所有时间段之后,返回最小开始时间和结果类型2
        if (maxEndTime == null) {
            return new Object[]{2, null, minStartTime};
        }

        // 如果最小开始时间为空,则说明datePoint在所有时间段之前,返回最大结束时间和结果类型2
        if (minStartTime == null) {
            return new Object[]{2, maxEndTime, null};
        }

        // 如果最大结束时间和最小开始时间都不为空,则返回它们以及结果类型2
        return new Object[]{2, maxEndTime, minStartTime};
    }


    /**
     * 时间段取交集
     * 该函数首先判断输入参数 timePeriods 是否为空,如果为空则返回 null。接下来遍历 timePeriods,
     * 从第一个时间段开始,不断更新已合并的时间段的起始时间和结束时间。
     * 如果当前时间段和已合并的时间段没有交集,则返回空数组。如果所有时间段都被合并,最终返回合并后的时间段。
     * @param timePeriods
     * @return
     */
    public static Date[] concentrateTimePeriods(List<Date[]> timePeriods) {
        if (timePeriods == null || timePeriods.size() == 0) {
            return null;
        }
        Date start = timePeriods.get(0)[0];
        Date end = timePeriods.get(0)[1];
        for (int i = 1; i < timePeriods.size(); i++) {
            Date[] period = timePeriods.get(i);
            if (period[0].after(end) || period[1].before(start)) {
                // 当前时间段和已合并的时间段没有交集,返回空数组
                return new Date[0];
            }
            start = period[0].after(start) ? period[0] : start;
            end = period[1].before(end) ? period[1] : end;
        }
        return new Date[]{start, end};
    }

    /**
     * 主函数,用于测试
     */
    public static void main(String[] args) throws ParseException {
//        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
//        // 初始化时间段集合
//        Date[] date1 = {sdf.parse("2023-02-10 09:00:00"), sdf.parse("2023-02-10 10:00:00")};
//        Date[] date2 = {sdf.parse("2023-02-10 10:00:00"), sdf.parse("2023-02-10 12:00:00")};
//        Date[] date3 = {sdf.parse("2023-02-10 08:00:00"), sdf.parse("2023-02-10 13:00:00")};
//        List<Date[]> timePeriods = new ArrayList<>();
//        timePeriods.add(date1);
//        timePeriods.add(date2);
//        timePeriods.add(date3);
//        List<Date[]> result1 = mergeTimePeriods(timePeriods);
//        printResult(result1);
//        System.out.println("-------------------------------");
//        // 初始化删除时间段集合
//        Date[] deleteDate1 = {sdf.parse("2023-02-10 09:00:00"), sdf.parse("2023-02-10 12:00:00")};
//        List<Date[]> deleteTimePeriods = new ArrayList<>();
//        deleteTimePeriods.add(deleteDate1);
//
//        List<Date[]> result2 = subtractTimePeriods(result1, deleteTimePeriods);
//        printResult(result2);
//        // 调用函数
//        Object[] result = getTimePeriodPosition(subtractTimePeriods(mergeTimePeriods(timePeriods), deleteTimePeriods), sdf.parse("2023-02-10 08:00:01"));
//        // 打印结果
//        System.out.println("-------------------------------");
//        System.out.println(Arrays.toString(result));



        //------------------------------------------

//        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
//        List<Date[]> timePeriods = new ArrayList<>();
//        timePeriods.add(new Date[] { sdf.parse("2022-02-13 02:00:00"), sdf.parse("2022-02-13 12:00:00") });
//        timePeriods.add(new Date[] { sdf.parse("2022-02-13 01:00:00"), sdf.parse("2022-02-13 11:00:00") });
//        timePeriods.add(new Date[] { sdf.parse("2022-02-13 00:00:00"), sdf.parse("2022-02-13 10:00:00") });
//
//        Date[] result = concentrateTimePeriods(timePeriods);
//
//        System.out.println("原始时间段:");
//        for (Date[] period : timePeriods) {
//            System.out.println(sdf.format(period[0]) + " - " + sdf.format(period[1]));
//        }
//
//        System.out.println("合成时间段:");
//        System.out.println(sdf.format(result[0]) + " - " + sdf.format(result[1]));

    }

    private static void printResult(List<Date[]> result) {
        for (int i = 0; i < result.size(); i++) {
            Date[] item = result.get(i);
            System.out.println(DateUtil.formatDateTime(item[0]) + "--->" + DateUtil.formatDateTime(item[1]));
        }
    }


}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值