Учитывая два списка диапазонов дат, как найти непересекающиеся даты в Java - PullRequest
0 голосов
/ 03 июля 2018

Я не уверен, правильно ли я понял заголовок, но это то, чего я пытаюсь достичь. Рассмотрим следующие два класса: 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;
}

Ответы [ 2 ]

0 голосов
/ 03 июля 2018

Если мое понимание остается в силе: вы пытаетесь найти все доступные периоды, когда есть несколько отсутствующих списков периодов

Тогда есть полное решение, которое нужно решить, когда существует множество отсутствующих списков периодов:

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. Но чтобы решить конкретную проблему, я обновил решение на основе потребностей ОП.

Как указывают другие комментарии, почему три разных типов? Понятия не имею ...

0 голосов
/ 03 июля 2018

Вы можете перебирать списки AbsentPeriod & UnavailablePeriod, используя функцию ниже, чтобы проверить, перекрываются ли даты, если нет, то добавить ее в список результатов:

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;
}
...