diff --git a/core-java-modules/core-java-8-datetime-2/src/main/java/com/baeldung/daterangeoverlap/DateRangeOverlapChecker.java b/core-java-modules/core-java-8-datetime-2/src/main/java/com/baeldung/daterangeoverlap/DateRangeOverlapChecker.java new file mode 100644 index 0000000000..9b898c87f5 --- /dev/null +++ b/core-java-modules/core-java-8-datetime-2/src/main/java/com/baeldung/daterangeoverlap/DateRangeOverlapChecker.java @@ -0,0 +1,46 @@ +package com.baeldung.daterangeoverlap; + +import java.time.LocalDate; +import java.util.Calendar; + +import org.joda.time.DateTime; +import org.joda.time.Interval; + +public class DateRangeOverlapChecker { + + public static boolean isOverlapUsingCalendarAndDuration(Calendar start1, Calendar end1, Calendar start2, Calendar end2) { + long overlap = Math.min(end1.getTimeInMillis(), end2.getTimeInMillis()) - Math.max(start1.getTimeInMillis(), start2.getTimeInMillis()); + return overlap >= 0; + } + + public static boolean isOverlapUsingLocalDateAndDuration(LocalDate start1, LocalDate end1, LocalDate start2, LocalDate end2) { + long overlap = Math.min(end1.toEpochDay(), end2.toEpochDay()) - Math.max(start1.toEpochDay(), start2.toEpochDay()); + return overlap >= 0; + } + + public static boolean isOverlapUsingJodaTime(DateTime start1, DateTime end1, DateTime start2, DateTime end2) { + Interval interval1 = new Interval(start1, end1); + Interval interval2 = new Interval(start2, end2); + return interval1.overlaps(interval2); + } + + public static boolean isOverlapUsingCalendarAndCondition(Calendar start1, Calendar end1, Calendar start2, Calendar end2) { + return !(end1.before(start2) || start1.after(end2)); + } + + public static boolean isOverlapUsingLocalDateAndCondition(LocalDate start1, LocalDate end1, LocalDate start2, LocalDate end2) { + return !(end1.isBefore(start2) || start1.isAfter(end2)); + } + + public static boolean isOverlapUsingCalendarAndFindMin(Calendar start1, Calendar end1, Calendar start2, Calendar end2) { + long overlap1 = Math.min(end1.getTimeInMillis() - start1.getTimeInMillis(), end1.getTimeInMillis() - start2.getTimeInMillis()); + long overlap2 = Math.min(end2.getTimeInMillis() - start2.getTimeInMillis(), end2.getTimeInMillis() - start1.getTimeInMillis()); + return Math.min(overlap1, overlap2) / (24 * 60 * 60 * 1000) >= 0; + } + + public static boolean isOverlapUsingLocalDateAndFindMin(LocalDate start1, LocalDate end1, LocalDate start2, LocalDate end2) { + long overlap1 = Math.min(end1.toEpochDay() - start1.toEpochDay(), end1.toEpochDay() - start2.toEpochDay()); + long overlap2 = Math.min(end2.toEpochDay() - start2.toEpochDay(), end2.toEpochDay() - start1.toEpochDay()); + return Math.min(overlap1, overlap2) >= 0; + } +} diff --git a/core-java-modules/core-java-8-datetime-2/src/test/java/com/baeldung/daterangeoverlap/DateRangeOverlapCheckerUnitTest.java b/core-java-modules/core-java-8-datetime-2/src/test/java/com/baeldung/daterangeoverlap/DateRangeOverlapCheckerUnitTest.java new file mode 100644 index 0000000000..5811cb6552 --- /dev/null +++ b/core-java-modules/core-java-8-datetime-2/src/test/java/com/baeldung/daterangeoverlap/DateRangeOverlapCheckerUnitTest.java @@ -0,0 +1,158 @@ +package com.baeldung.daterangeoverlap; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import java.time.LocalDate; +import java.util.Calendar; + +import org.joda.time.DateTime; +import org.junit.Test; + +public class DateRangeOverlapCheckerUnitTest { + + @Test + public void givenPartialOverlappingRanges_thenReturnsTrue() { + Calendar start1 = Calendar.getInstance(); + start1.set(2024, 11, 15); + Calendar end1 = Calendar.getInstance(); + end1.set(2024, 11, 20); + + Calendar start2 = Calendar.getInstance(); + start2.set(2024, 11, 18); + Calendar end2 = Calendar.getInstance(); + end2.set(2024, 11, 22); + + LocalDate startLD1 = LocalDate.of(2024, 12, 15); + LocalDate endLD1 = LocalDate.of(2024, 12, 20); + + LocalDate startLD2 = LocalDate.of(2024, 12, 18); + LocalDate endLD2 = LocalDate.of(2024, 12, 22); + + DateTime startJT1 = new DateTime(2024, 12, 15, 0, 0); + DateTime endJT1 = new DateTime(2024, 12, 20, 0, 0); + + DateTime startJT2 = new DateTime(2024, 12, 18, 0, 0); + DateTime endJT2 = new DateTime(2024, 12, 22, 0, 0); + + assertTrue(DateRangeOverlapChecker.isOverlapUsingCalendarAndDuration(start1, end1, start2, end2)); + assertTrue(DateRangeOverlapChecker.isOverlapUsingLocalDateAndDuration(startLD1, endLD1, startLD2, endLD2)); + + assertTrue(DateRangeOverlapChecker.isOverlapUsingCalendarAndCondition(start1, end1, start2, end2)); + assertTrue(DateRangeOverlapChecker.isOverlapUsingLocalDateAndCondition(startLD1, endLD1, startLD2, endLD2)); + + assertTrue(DateRangeOverlapChecker.isOverlapUsingCalendarAndFindMin(start1, end1, start2, end2)); + assertTrue(DateRangeOverlapChecker.isOverlapUsingLocalDateAndFindMin(startLD1, endLD1, startLD2, endLD2)); + + assertTrue(DateRangeOverlapChecker.isOverlapUsingJodaTime(startJT1, endJT1, startJT2, endJT2)); + } + + @Test + public void givenFullOverlappingRanges_thenReturnsTrue() { + Calendar start1 = Calendar.getInstance(); + start1.set(2024, 11, 15); + Calendar end1 = Calendar.getInstance(); + end1.set(2024, 11, 20); + + Calendar start2 = Calendar.getInstance(); + start2.set(2024, 11, 16); + Calendar end2 = Calendar.getInstance(); + end2.set(2024, 11, 18); + + LocalDate startLD1 = LocalDate.of(2024, 12, 15); + LocalDate endLD1 = LocalDate.of(2024, 12, 20); + + LocalDate startLD2 = LocalDate.of(2024, 12, 16); + LocalDate endLD2 = LocalDate.of(2024, 12, 18); + + DateTime startJT1 = new DateTime(2024, 12, 15, 0, 0); + DateTime endJT1 = new DateTime(2024, 12, 20, 0, 0); + + DateTime startJT2 = new DateTime(2024, 12, 16, 0, 0); + DateTime endJT2 = new DateTime(2024, 12, 18, 0, 0); + + assertTrue(DateRangeOverlapChecker.isOverlapUsingCalendarAndDuration(start1, end1, start2, end2)); + assertTrue(DateRangeOverlapChecker.isOverlapUsingLocalDateAndDuration(startLD1, endLD1, startLD2, endLD2)); + + assertTrue(DateRangeOverlapChecker.isOverlapUsingCalendarAndCondition(start1, end1, start2, end2)); + assertTrue(DateRangeOverlapChecker.isOverlapUsingLocalDateAndCondition(startLD1, endLD1, startLD2, endLD2)); + + assertTrue(DateRangeOverlapChecker.isOverlapUsingCalendarAndFindMin(start1, end1, start2, end2)); + assertTrue(DateRangeOverlapChecker.isOverlapUsingLocalDateAndFindMin(startLD1, endLD1, startLD2, endLD2)); + + assertTrue(DateRangeOverlapChecker.isOverlapUsingJodaTime(startJT1, endJT1, startJT2, endJT2)); + } + + @Test + public void givenConsecutiveRanges_thenReturnsFalse() { + Calendar start1 = Calendar.getInstance(); + start1.set(2024, 11, 15); + Calendar end1 = Calendar.getInstance(); + end1.set(2024, 11, 20); + + Calendar start2 = Calendar.getInstance(); + start2.set(2024, 11, 21); + Calendar end2 = Calendar.getInstance(); + end2.set(2024, 11, 24); + + LocalDate startLD1 = LocalDate.of(2024, 12, 15); + LocalDate endLD1 = LocalDate.of(2024, 12, 20); + + LocalDate startLD2 = LocalDate.of(2024, 12, 21); + LocalDate endLD2 = LocalDate.of(2024, 12, 24); + + DateTime startJT1 = new DateTime(2024, 12, 15, 0, 0); + DateTime endJT1 = new DateTime(2024, 12, 20, 0, 0); + + DateTime startJT2 = new DateTime(2024, 12, 21, 0, 0); + DateTime endJT2 = new DateTime(2024, 12, 24, 0, 0); + + assertFalse(DateRangeOverlapChecker.isOverlapUsingCalendarAndDuration(start1, end1, start2, end2)); + assertFalse(DateRangeOverlapChecker.isOverlapUsingLocalDateAndDuration(startLD1, endLD1, startLD2, endLD2)); + + assertFalse(DateRangeOverlapChecker.isOverlapUsingCalendarAndCondition(start1, end1, start2, end2)); + assertFalse(DateRangeOverlapChecker.isOverlapUsingLocalDateAndCondition(startLD1, endLD1, startLD2, endLD2)); + + assertFalse(DateRangeOverlapChecker.isOverlapUsingCalendarAndFindMin(start1, end1, start2, end2)); + assertFalse(DateRangeOverlapChecker.isOverlapUsingLocalDateAndFindMin(startLD1, endLD1, startLD2, endLD2)); + + assertFalse(DateRangeOverlapChecker.isOverlapUsingJodaTime(startJT1, endJT1, startJT2, endJT2)); + } + + @Test + public void givenZeroRangeRanges_thenReturnsTrue() { + Calendar start1 = Calendar.getInstance(); + start1.set(2024, 11, 15); + Calendar end1 = Calendar.getInstance(); + end1.set(2024, 11, 20); + + Calendar start2 = Calendar.getInstance(); + start2.set(2024, 11, 20); + Calendar end2 = Calendar.getInstance(); + end2.set(2024, 11, 20); + + LocalDate startLD1 = LocalDate.of(2024, 12, 15); + LocalDate endLD1 = LocalDate.of(2024, 12, 20); + + LocalDate startLD2 = LocalDate.of(2024, 12, 20); + LocalDate endLD2 = LocalDate.of(2024, 12, 20); + + DateTime startJT1 = new DateTime(2024, 12, 15, 0, 0); + DateTime endJT1 = new DateTime(2024, 12, 20, 0, 0); + + DateTime startJT2 = new DateTime(2024, 12, 20, 0, 0); + DateTime endJT2 = new DateTime(2024, 12, 20, 0, 0); + + assertTrue(DateRangeOverlapChecker.isOverlapUsingCalendarAndDuration(start1, end1, start2, end2)); + assertTrue(DateRangeOverlapChecker.isOverlapUsingLocalDateAndDuration(startLD1, endLD1, startLD2, endLD2)); + + assertTrue(DateRangeOverlapChecker.isOverlapUsingCalendarAndCondition(start1, end1, start2, end2)); + assertTrue(DateRangeOverlapChecker.isOverlapUsingLocalDateAndCondition(startLD1, endLD1, startLD2, endLD2)); + + assertTrue(DateRangeOverlapChecker.isOverlapUsingCalendarAndFindMin(start1, end1, start2, end2)); + assertTrue(DateRangeOverlapChecker.isOverlapUsingLocalDateAndFindMin(startLD1, endLD1, startLD2, endLD2)); + + //the overlaps method considers two intervals as overlapping only if they have a non-zero duration. + assertFalse(DateRangeOverlapChecker.isOverlapUsingJodaTime(startJT1, endJT1, startJT2, endJT2)); + } +}