Here’s my suggestion. The following method “subtracts” two lists of schedules by eliminating the date intervals from the first list that are in the second list. It uses a double loop where it first iterates over the schedules in the second list, those that should be subtracted. For each such schedule it subtracts it from each schedule from the first list, building a new list of the resulting schedules.
public static List<Schedule> scheduleListDiff(
List<Schedule> schedules, List<Schedule> schedulesToExclude) {
// eliminate dates from schedulesToExclude one schdule at a time
for (Schedule toExclude : schedulesToExclude) {
List<Schedule> result = new ArrayList<>();
for (Schedule originalSchedule : schedules) {
result.addAll(originalSchedule.notPresentIn(toExclude));
}
schedules = result;
}
return schedules;
}
You may call it this way
List<Schedule> notPresentInPreviousSchedule
= scheduleListDiff(previousSchedules, newSchedules);
With the lists from your question the result is the desired
1 -- 10-Jan-2014 -- 11-Jan-2014
1 -- 16-Jan-2014 -- 20-Jan-2014
I have fitted the Schedule
class with an auxiliary method notPresentIn()
to perform the actual comparison:
/** @return a list of 0, 1 or 2 schedules with the dates from this schedule that are not in other */
List<Schedule> notPresentIn(Schedule other) {
if (other.end.isBefore(start) || end.isBefore(other.start)) { // no overlap
return Collections.singletonList(this);
}
// now we know there is an overlap
List<Schedule> result = new ArrayList<>(2);
if (start.isBefore(other.start)) { // need to include day/s from the first part of this
// this bit must end the day before other.start
result.add(new Schedule(objectiveId, start, other.start.minusDays(1)));
}
if (end.isAfter(other.end)) { // need day/s from the last part
result.add(new Schedule(objectiveId, other.end.plusDays(1), end));
}
return result;
}
I have not tested thoroughly, there could easily be a bug somewhere, but I hope this gets you started.
I have not considered efficiency. If you have millions of schedules you may benefit from a more complicated algorithm that sorts the schedules chronologically first so you don’t need to compare every schedule from one list to every schedule of the other. With a few hundred schedules I heavily doubt that you need care.
I am using java.time.LocalDate
for the dates in the Schedule
class:
int objectiveId;
// dates are inclusive; end is on or after start
LocalDate start;
LocalDate end;
Edit: I ran my code on the sample data from the duplicate question find out cancelled period from given date. That sample has two new sample schedules within one previous schedule. So this previous schedule should be split up into three. The result was:
107 -- 10 May 2016 -- 11 May 2016
107 -- 14 May 2016 -- 15 May 2016
107 -- 19 May 2016 -- 20 May 2016
This works because each iteration in scheduleListDiff()
uses the result from the previous iteration, so first the schedule is split into two, next iteration one of the two is further split.