Java 日期区间与日期区间集合的差集 代码实现

项目中遇到一个需求,就是过滤出一个日期区间与一个日期区间集合的差集。
例如:
已有日期区间:[2019-07-01 00:00:00~2019-07-31 23:59:59]
日期区间集合为:[{2019-07-01 00:00:00, 2019-07-10 23:59:59}, {2019-07-15 00:00:00, 2019-07-20 23:59:59}, {2019-07-22 00:00:00, 2019-07-25 23:59:59}]
目标结果:[{2019-07-11 00:00:00, 2019-07-14 23:59:59}, {2019-07-21 00:00:00, 2019-07-21 23:59:59}, {2019-07-26 00:00:00, 2019-07-31 23:59:59}]

0. 解决思路

  1. 测试数据准备
    1.1 已知日期区间对象 DateObject date
    1.2 初始化日期区间集合 List<DateObject> list
  2. 将日期集合list中的所有日期区间合并
  3. 已知日期区间datelist求交集(找出list中日期区间坐落到date的交集intersection
    交集
  4. 已知日期区间date再与交集intersection求差集differences
    差集

1. 定义包含[开始日期~结束日期]的对象DateObject

/**
 * 包含开始日期和结束日期的对象
 */
public class DateObject {
    private Date startDate;
    private Date endDate;

    public DateObject(Date startDate, Date endDate) {
        this.startDate = startDate;
        this.endDate = endDate;
    }

    public Date getStartDate() {
        return startDate;
    }

    public void setStartDate(Date startDate) {
        this.startDate = startDate;
    }

    public Date getEndDate() {
        return endDate;
    }

    public void setEndDate(Date endDate) {
        this.endDate = endDate;
    }

    @Override
    public String toString() {
        return "DateObject{startDate=" + startDate + ", endDate=" + endDate +  "}";
    }
}

2. 初始化DateObject集合对象

    /**
     * 初始化DateObject集合对象
     *
     * @return 集合对象
     * @throws Exception 异常
     */
    private List<DateObject> init() throws Exception {
        List<DateObject> list = new ArrayList<>();
        SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        list.add(new DateObject(format.parse("2019-07-01 00:00:00"), format.parse("2019-07-10 23:59:59")));
        list.add(new DateObject(format.parse("2019-07-15 00:00:00"), format.parse("2019-07-20 23:59:59")));
        list.add(new DateObject(format.parse("2019-07-22 00:00:00"), format.parse("2019-07-25 23:59:59")));
        list.sort(Comparator.comparing(DateObject::getStartDate));
        return list;
    }

3. DateObject集合中的日期区间合并

    /**
     * 时间区间合并
     *
     * @param list 日期集合
     * @return 合并后日期集合
     */
    private List<DateObject> merge(List<DateObject> list) {
        if (list == null || list.size() == 0) {
            return new ArrayList<>();
        }

        List<DateObject> result = new ArrayList<>();
        DateObject first = list.get(0);
        for (int i = 1; i < list.size(); i++) {
            DateObject next = list.get(i);
            // 合并区间,时间精确到秒(差1秒则为连续)
            if (next.getStartDate().getTime() < first.getEndDate().getTime() || next.getStartDate().getTime() - first.getEndDate().getTime() == 1000) {
                first.setStartDate(new Date(Math.min(first.getStartDate().getTime(), next.getStartDate().getTime())));
                first.setEndDate(new Date(Math.max(first.getEndDate().getTime(), next.getEndDate().getTime())));
            } else {
                // 没有交集,直接添加
                result.add(first);
                first = next;
            }
        }
        result.add(first);
        return result;
    }

4. 已知日期区间date与日期区间集合list求交集

分析:集合中时间区间遍历与date相比较,两个时间区间会有4中相交的情况,此处只讨论有交集的情况。
两个时间区间相交的情况

   /**
     * 求开放时间区间与时间区间的所有交集
     *
     * @param dateObject 日期对象
     * @param list       日期列表
     * @return 日期交集
     */
    private List<DateObject> intersection(DateObject dateObject, List<DateObject> list) {
        List<DateObject> result = new ArrayList<>();
        for (DateObject date : list) {
            // 时间区间全部在开放时间区间内:ad.startDate <= open.startDate < open.endDate <= ad.endDate
            // 则[open.startDate, open.endDate] 均不开放
            if (date.getStartDate().getTime() <= dateObject.getStartDate().getTime()
                    && date.getEndDate().getTime() >= dateObject.getEndDate().getTime()) {
                // 开放时间区间已全部占用
                break;
            }
            // 时间区间全部在开放时间区间内:open.startDate <= ad.startDate < ad.endDate <= open.endDate
            else if (date.getStartDate().getTime() >= dateObject.getStartDate().getTime()
                    && date.getEndDate().getTime() <= dateObject.getEndDate().getTime()) {
                result.add(new DateObject(date.getStartDate(), date.getEndDate()));
            }
            // 结束时间在开放时间区间内:ad.startDate <= open.startDate <= ad.endDate <= open.endDate
            else if (date.getStartDate().getTime() <= dateObject.getStartDate().getTime()
                    && date.getEndDate().getTime() >= dateObject.getStartDate().getTime()
                    && date.getEndDate().getTime() <= dateObject.getEndDate().getTime()) {
                result.add(new DateObject(dateObject.getStartDate(), date.getEndDate()));
            }
            // 开始时间在开放时间区间内:open.startDate <= ad.startDate <= open.endDate <= ad.endDate
            else if (date.getEndDate().getTime() > dateObject.getEndDate().getTime()) {
                result.add(new DateObject(date.getStartDate(), dateObject.getEndDate()));
            }
        }
        return result;
    }

5. 已知日期区间date再与交集intersection求差集differences

集合intersection中的日期区间已经排好序了,第一个元素的结束日期与下一个元素的开始日期就是要的结果,以此类推

   /**
     * 求差集
     *
     * @param dateObject 日期对象
     * @param list       日期列表
     * @return 差集
     */
    private List<DateObject> differences(DateObject dateObject, List<DateObject> list) {
        List<DateObject> result = new ArrayList<>();
        DateObject first = list.get(0);
        if (list.size() == 1) {
            if (dateObject.getStartDate().getTime() < first.getStartDate().getTime()) {
                result.add(new DateObject(dateObject.getStartDate(), DateUtils.getEndTimeOfDay(DateUtils.plusDays(first.getStartDate(), -1))));
            }
            if (dateObject.getEndDate().getTime() - first.getEndDate().getTime() > 1000) {
                result.add(new DateObject(DateUtils.getFirstTimeOfDay(DateUtils.plusDays(first.getEndDate(), 1)), DateUtils.getEndTimeOfDay(dateObject.getEndDate())));
            }
        } else {
            for (int i = 1; i < list.size(); i++) {
                DateObject next = list.get(i);
                result.add(new DateObject(DateUtils.getFirstTimeOfDay(DateUtils.plusDays(first.getEndDate(), 1)), DateUtils.getEndTimeOfDay(DateUtils.plusDays(next.getStartDate(), -1))));
                first = next;
            }
            DateObject last = list.get(list.size() - 1);
            if (dateObject.getEndDate().getTime() > last.getEndDate().getTime()) {
                result.add(new DateObject(DateUtils.getFirstTimeOfDay(DateUtils.plusDays(last.getEndDate(), 1)), DateUtils.getEndTimeOfDay(dateObject.getEndDate())));
            }
        }
        return result;
    }

6. 测试类

    @Test
    public void test1() throws Exception {
        SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        // 时间区间
        DateObject open = new DateObject(format.parse("2019-07-01 00:00:00"), format.parse("2019-07-31 23:59:59"));

        // 时间区间列表合并
        List<DateObject> merge = merge(init());
        System.out.println("时间区间求并集:" + JSONObject.toJSONStringWithDateFormat(merge, "yyyy-MM-dd HH:mm:ss"));
        // 求交集
        List<DateObject> intersection = intersection(open, merge);
        System.out.println("时间区间求交集:" + JSONObject.toJSONStringWithDateFormat(intersection, "yyyy-MM-dd HH:mm:ss"));
        // 求差集
        if (intersection.isEmpty()) {
            System.out.println("该区间不可再选:" + JSONObject.toJSONStringWithDateFormat(open, "yyyy-MM-dd HH:mm:ss"));
            return;
        }
        List<DateObject> list = differences(open, intersection);
        System.out.println("时间区间求差集:" + JSONObject.toJSONStringWithDateFormat(list, "yyyy-MM-dd HH:mm:ss"));
    }

测试结果:

时间区间求并集:[{"endDate":"2019-07-10 23:59:59","startDate":"2019-07-01 00:00:00"},{"endDate":"2019-07-20 23:59:59","startDate":"2019-07-15 00:00:00"},{"endDate":"2019-07-25 23:59:59","startDate":"2019-07-22 00:00:00"}]
时间区间求交集:[{"endDate":"2019-07-10 23:59:59","startDate":"2019-07-01 00:00:00"},{"endDate":"2019-07-20 23:59:59","startDate":"2019-07-15 00:00:00"},{"endDate":"2019-07-25 23:59:59","startDate":"2019-07-22 00:00:00"}]
时间区间求差集:[{"endDate":"2019-07-14 23:59:59","startDate":"2019-07-11 00:00:00"},{"endDate":"2019-07-21 23:59:59","startDate":"2019-07-21 00:00:00"},{"endDate":"2019-07-31 23:59:59","startDate":"2019-07-26 00:00:00"}]

7. DateUtils 类

  public class DateUtils {

    /**
     * 获取date当天的最后时间
     *
     * @param date 日期
     * @return date当天的最后时间
     */
    public static Date getEndTimeOfDay(Date date) {
        LocalDateTime localDateTime = LocalDateTime.ofInstant(date.toInstant(), ZoneId.systemDefault());
        return Date.from(LocalDateTime.of(localDateTime.toLocalDate(), LocalTime.MAX).atZone(ZoneId.systemDefault()).toInstant());
    }

    /**
     * 获取date当天的开始日期
     *
     * @param date 日期
     * @return date当天的开始日期
     */
    public static Date getFirstTimeOfDay(Date date) {
        LocalDateTime localDateTime = LocalDateTime.ofInstant(date.toInstant(), ZoneId.systemDefault());
        return Date.from(LocalDateTime.of(localDateTime.toLocalDate(), LocalTime.MIN).atZone(ZoneId.systemDefault()).toInstant());
    }
}
  • 4
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 5
    评论
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值