3

I am not sure if I got the heading correct, but this is what I am trying to achieve. Consider the following two classes, AbsentPeriod, UnavailablePeriod.

class AbsentPeriod
{
    Date startDate;
    Date endDate;
}

class UnavailablePeriod
{
    Date startDate;
    Date endDate;
}

Given

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

Find

List<AvailablePeriod> 

which has the start and end dates that have dates do not overlap the dates from the Absent and unavailable periods; which are in between given StartDate and endDate

class AvailablePeriod
{
   Date startDate;
   Date endDate;
}
Vinay
  • 93
  • 6
  • 1
    What have you tried so far? – Zephyr Jul 03 '18 at 03:37
  • Start by sorting them – MadProgrammer Jul 03 '18 at 03:39
  • 2
    Well, while it's using JodaTime, it should be easily convertible to the Java Date/Time APIs - [for example](https://stackoverflow.com/questions/20677541/date-range-in-date-range/20678485#20678485) – MadProgrammer Jul 03 '18 at 03:41
  • 1
    [Another possible example](https://stackoverflow.com/questions/25735407/validate-item-fall-within-start-date-and-end-date/25735601#25735601) and [another](https://stackoverflow.com/questions/31044827/how-to-determine-a-date-in-between-friday-and-sunday-of-the-week-at-a-particular/31045131#31045131) and [another](https://stackoverflow.com/questions/34348164/find-a-space-of-time-in-a-set-of-dates/34348416#34348416) – MadProgrammer Jul 03 '18 at 03:52
  • 1
    A "period" is a period, period (sorry) - while in you example it's adds some possible clarification, try and avoid creating data types that represent the same concept but for which are named for particular purposes, this is why you have variable names ;) - I might be tempted to use `Range` or `Span` instead, as "period" seems to reflect a single point – MadProgrammer Jul 03 '18 at 03:54
  • Are these 3 classes share common interface ? – Amith Kumar Jul 03 '18 at 03:55
  • @MadProgrammer is right, you just need one `Period` class. You may use it for all three purposes. – Ole V.V. Jul 03 '18 at 05:02
  • No. I can’t. That’s a requirement I have..all three are different set of dates I need to calculate the available dates based on the absentPeriods and unavailablePeriods both are given to me. – Vinay Jul 03 '18 at 05:21
  • 1
    It pretty much doesn't matter if the ranges are date ranges or int ranges or float ranges or character ranges, does it? – kumesana Jul 03 '18 at 06:34
  • @kumsena not sure, in the end based on the Available Periods I also need to calculate the total number of available days, so I think it should be dates. – Vinay Jul 03 '18 at 18:06
  • 1
    @Vinay Yes, you have "three" different uses of a "date range" - but you still only have one concept of a "date range" - personally, I'd start with a `interface`, if needed create three more interfaces to cover your "supposed requirements" and then use a default implementation which can cover all of them - but I still think, a single "date range" object assigned to three different lists will do the same job more simply – MadProgrammer Jul 03 '18 at 19:56
  • When you do this type of thing, just draw some horizontal lines to represent the ranges - it only comes down to a few combos like range a starts before and ends after b etc. Then work out the logic from that. – JGFMK Jul 04 '18 at 00:07

2 Answers2

0

You can iterate through the lists of AbsentPeriod & UnavailablePeriod, with below function to check if dates are overlapping, if not then add it to the result list:

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;
}
Amith Kumar
  • 4,400
  • 1
  • 21
  • 28
0

If my understanding stands: you are trying to find out all available periods when there are some absent-like period lists

Then there is a complete solution to solve when there are lots of absent-like period lists:

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 {
    }

}

For the input:

  • Absent period list - 1: [0, 1], [4, 6], [2, 3]
  • Absent period list - 2: [0, 2], [5, 7]

The final result will be:

  • Available period list: [3, 4]

You can try more absent period lists if you need.

Updated

I have no idea why the OP need three different types of Period/Interval here. But to solve the specific problem, I updated the solution based on the OP's needs.

Just as other comments point out, why three different types? I have no clue...

Hearen
  • 7,420
  • 4
  • 53
  • 63
  • true. that's where I am stuck with absent-like periods. will try this tomorrow. looks like it could work. – Vinay Jul 03 '18 at 04:25
  • Any confusion? If no, please accept it as an answer. – Hearen Jul 03 '18 at 04:28
  • Here you are using a single Class MyPeriod, but i have three different classes, though have same content Start Date and End Date, we need to differentiate them with AbsentPeriod, UnavailablePeriod and AvailablePeriod. – Vinay Jul 03 '18 at 18:04
  • 1
    @Vinay No you don't, not really. You just need a single "date range" object, which can be assigned to a series of containers/`List` which would differentiate them - if "really" required, I might use a "type" identifier or create a series of `interfaces which can be used to "differentiate" the class by type, but that's me. Having said that, no single answer will give you every thing you want - you're going to have pull concepts and ideas from a number of different solutions o make your own, it's the hall mark of a good developer – MadProgrammer Jul 03 '18 at 19:59
  • @Vinay You are...Hahah, I just wake up and saw the reply of MadProgramer. U... (made him not happy... hahah) I will update the solutions further. Have totally no idea why you need **different** types for the solution. – Hearen Jul 03 '18 at 23:46
  • @Vinay I updated the solution to include **three** types. But you'd understand normally there is no such need to create them since they are the same and we can just use only **one single** type to represent them all and that's why we call it **object-oriented** java. – Hearen Jul 03 '18 at 23:58