在Java中,如何找到两个日期范围列表中不重叠的日期?

3
我不确定标题是否正确,但这是我试图实现的内容。考虑以下两个类,AbsentPeriod和UnavailablePeriod。
class AbsentPeriod
{
    Date startDate;
    Date endDate;
}

class UnavailablePeriod
{
    Date startDate;
    Date endDate;
}

考虑到

Date StartDate // starting point
Date EndDate //ending point
List<AbsentPeriod> absentperiods
List<UnavailablePeriods> unavailablePeriods

寻找

List<AvailablePeriod> 

此处需要一个起始日期和结束日期,这些日期不与缺席和不可用期间的日期重叠;这些日期应该在给定的StartDate和endDate之间。

class AvailablePeriod
{
   Date startDate;
   Date endDate;
}

1
你目前尝试了什么? - Zephyr
2
嗯,虽然它使用了JodaTime,但应该很容易转换为Java日期/时间API - 例如 - MadProgrammer
1
“Period”就是一个周期,没错(抱歉)。虽然在你的例子中它增加了一些可能的澄清,但尽量避免创建代表相同概念但命名为特定目的的数据类型,这就是为什么你有变量名的原因;)-我可能会倾向于使用“范围”或“跨度”,因为“期间”似乎反映了一个单一点。 - MadProgrammer
1
无论范围是日期范围、整数范围、浮点数范围还是字符范围,它都没什么区别,对吧? - kumesana
显示剩余7条评论
2个回答

0

您可以使用以下函数遍历 AbsentPeriodUnavailablePeriod 列表,检查日期是否重叠,如果没有,则将其添加到结果列表中:

public static boolean dateRangeOverlap(Date givenStartDate, Date givenEndDate, Date listItemStartDate, Date listItemEndDate)
{
    boolean result = false;
    if (givenStartDate !=null && givenEndDate !=null && listItemStartDate !=null && listItemEndDate != null){
        result = (givenStartDate.getTime() <= listItemEndDate.getTime()) && (givenEndDate.getTime() >= listItemStartDate.getTime()); 
    }
    return result;
}

0
如果我的理解正确:您正在尝试找出所有“可用”的时间段,当有一些“类似缺席”的时间段列表时。
那么,当有许多“类似缺席”的时间段列表时,有一个完整的解决方案可以解决问题:
public class Extra_1_interval_merge {
    @Test
    public void testAvailablePeriod() {
        List<MyPeriod> absentPeriods0 = new ArrayList<>();
        absentPeriods0.add(makePeriod(LocalDate.now(), LocalDate.now().plusDays(1), PeriodType.ABSENT));
        absentPeriods0.add(makePeriod(LocalDate.now().plusDays(4), LocalDate.now().plusDays(6), PeriodType.ABSENT));
        absentPeriods0.add(makePeriod(LocalDate.now().plusDays(2), LocalDate.now().plusDays(3), PeriodType.ABSENT));


        List<MyPeriod> absentPeriods1 = new ArrayList<>();
        absentPeriods1.add(makePeriod(LocalDate.now(), LocalDate.now().plusDays(2), PeriodType.UNAVAILABLE));
        absentPeriods1.add(makePeriod(LocalDate.now().plusDays(5), LocalDate.now().plusDays(7), PeriodType.UNAVAILABLE));

        List<List<MyPeriod>> absentListList = new ArrayList<>();
        absentListList.add(absentPeriods0);
        absentListList.add(absentPeriods1);
        System.out.println(getAvailablePeriods(absentListList));
    }

    private List<MyPeriod> getAvailablePeriods(List<List<MyPeriod>> absentListList) {
        // Step - 1: Collect all periods;
        List<MyPeriod> tempList = new ArrayList<>();
        absentListList.stream().forEach(list -> tempList.addAll(list));

        // Step - 2: Sort the periods based on the startDate and then endDate;
        List<MyPeriod> absentList = tempList.stream().sorted((period1, period2) -> {
            if (!period1.startDate.isEqual(period2.startDate)) {
                return period1.startDate.compareTo(period2.startDate);
            } else {
                return period1.endDate.compareTo(period2.endDate);
            }
        }).collect(toList());

        // Step - 3: Merge all overlapped periods to form an one-dimension occupied period list;
        List<MyPeriod> mergedPeriods = new ArrayList<>();
        for (MyPeriod period : absentList) {
            if (mergedPeriods.isEmpty()) {
                mergedPeriods.add(period);
            } else {
                MyPeriod lastPeriod = mergedPeriods.get(mergedPeriods.size() - 1);
                if (!lastPeriod.endDate.isBefore(period.startDate)) {
                    if (lastPeriod.endDate.isBefore(period.endDate)) {
                        lastPeriod.endDate = period.endDate;
                    }
                } else {
                    mergedPeriods.add(period);
                }
            }
        }

        // Step - 4: Pick the periods from the occupied period list;
        List<MyPeriod> availablePeriods = new ArrayList<>();
        for (int i = 0, len = mergedPeriods.size(); i < len - 1; i++) {
            availablePeriods.add(makePeriod(mergedPeriods.get(i).endDate, mergedPeriods.get(i + 1).startDate, PeriodType.AVAILABLE));
        }
        return availablePeriods;
    }

    private MyPeriod makePeriod(LocalDate startDate, LocalDate endDate, PeriodType periodType) {
        MyPeriod thePeriod = null;
        switch (periodType) {
            case ABSENT:
                thePeriod = new AbsentPeriod();
                break;
            case UNAVAILABLE:
                thePeriod = new UnavailablePeriod();
                break;
            case AVAILABLE:
                thePeriod = new AvailablePeriod();
                break;
            default:
                thePeriod = new MyPeriod();
                break;
        }
        thePeriod.startDate = startDate;
        thePeriod.endDate = endDate;
        return thePeriod;
    }

    enum PeriodType {
        ABSENT,
        UNAVAILABLE,
        AVAILABLE;
    }

    class MyPeriod {
        LocalDate startDate;
        LocalDate endDate;

        @Override
        public String toString() {
            return String.format("Start: %s, End: %s", startDate, endDate);
        }
    }

    class AbsentPeriod extends MyPeriod {
    }

    class UnavailablePeriod extends MyPeriod {
    }

    class AvailablePeriod extends MyPeriod {
    }

}

对于输入:

  • 缺席期列表-1:[0,1],[4,6],[2,3]
  • 缺席期列表-2:[0,2],[5,7]

最终结果将为:

  • 可用期列表:[3, 4]

如果需要,您可以尝试更多的缺席期列表。

更新

我不知道为什么OP在这里需要三种不同类型的Period/Interval。但是为了解决具体问题,我根据OP的需求更新了解决方案。

正如其他评论所指出的那样,为什么要有三种不同的类型?我毫无头绪……


真的。这就是我卡住了,就像缺席一样的时期。明天会尝试这个。看起来有可能行得通。 - Vinay
有任何疑问吗?如果没有,请接受它作为答案。 - Hearen
在这里,您正在使用一个名为MyPeriod的类,但我有三个不同的类,尽管它们具有相同的开始日期和结束日期内容,我们需要用AbsentPeriod、UnavailablePeriod和AvailablePeriod来区分它们。 - Vinay
1
@Vinay 不,你不需要,真的。你只需要一个“日期范围”对象,它可以分配给一系列容器/List,这将使它们有所区别 - 如果“真的”需要,我可能会使用“类型”标识符或创建一系列接口,以便通过类型“区分”类,但这是我的做法。话虽如此,没有单一的答案能够满足你所有的需求 - 你需要从许多不同的解决方案中提取概念和想法来制定自己的方案,这是一个优秀开发者的标志。 - MadProgrammer
@Vinay,你是...哈哈,我刚醒来看到了MadProgramer的回复。我...(让他不高兴了...哈哈)我会进一步更新解决方案。完全不知道为什么你需要不同类型的解决方案。 - Hearen
@Vinay 我更新了解决方案,包括三种类型。但是你应该明白通常没有必要创建它们,因为它们都是相同的,我们只需要使用一个单一的类型来代表它们所有,这就是为什么我们称之为面向对象的Java。 - Hearen

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接