user2511882
user2511882

Reputation: 9152

How to check if time period is overlapping another time period irrespective of AM/PM

The following link: How to check a timeperiod is overlapping another time period in java is helpful to check if a given timestamp overlaps with another timestamp.

However, this only works if the two timestamp are either in AM or PM. What I want to check is, if a given timestamp is overlapping another timestamp irrespective if it's in AM or PM.

For example: If I have an Array of timestamps (in 24 hour format):

1:00 - 3:00

13:45 - 14:45

3:15 - 4:00.

The first two are technically overlapping since 13:45 - 14:45 falls between 1:00-3:00 (if converted into 12 hour format)

How do I check for the same? I want to essentially check if there is any overlap between the timestamps (+/- 30 mins)

Upvotes: 1

Views: 1324

Answers (1)

Anonymous
Anonymous

Reputation: 86324

Assuming that each interval is within either AM or PM

I am assuming that each interval is either completely within AM (00:00 through 12) or PM (12:00 through 00). I didn’t understand what you meant by “(+/- 30 mins)”, so I have ignored that.

As an aside I consider this an artificial challenge. In the real world 2 AM and 2 PM are not the same, they just happen to have identical representations on a 12 hour clock. Just as a flag pole and a person from Poland are not the same even though they both have the representation “Pole”.

As Sweeper suggested in a comment I am converting each interval to AM (if it was in PM) before comapring.

    LocalTime begin1 = LocalTime.of(1, 0);
    LocalTime end1 = LocalTime.of(3, 0);
    LocalTime begin2 = LocalTime.of(13, 45);
    LocalTime end2 = LocalTime.of(14, 45);

    // Convert interval 1 to AM
    if (begin1.get(ChronoField.AMPM_OF_DAY) == 1) { // PM
        begin1 = begin1.minusHours(12);
        end1 = end1.minusHours(12);
    }
    // validate
    if (end1.isBefore(begin1)) {
        throw new IllegalStateException("end1 " + end1 + " must not be before begin1 " + begin1);
    }
    if (end1.isAfter(LocalTime.NOON)) {
        throw new IllegalStateException("Interval 1 must be completely within either AM or PM");
    }

    // Convert interval 2 to AM
    if (begin2.get(ChronoField.AMPM_OF_DAY) == 1) {
        begin2 = begin2.minusHours(12);
        end2 = end2.minusHours(12);
    }
    // validate
    if (end2.isBefore(begin2)) {
        throw new IllegalStateException("end2 " + end2 + " must not be before begin2 " + begin2);
    }
    if (end2.isAfter(LocalTime.NOON)) {
        throw new IllegalStateException("Interval 2 must be completely within either AM or PM");
    }

    if (end2.isAfter(begin1) && end1.isAfter(begin2)) {
        System.out.println("They overlap");
    } else {
        System.out.println("They do not overlap");
    }

Output from this code is:

They overlap

Corner case: I am accepting an end time of 12:00 (noon) for an AM interval and of 00:00 for a PM interval. LocalTime.minusHours() has cyclic underflow, so subtracting 12 hours from 00:00 gives 12:00.

The code may be simpler and easier to find your way through if you define a TimePeriod class with fields for begin and end, a method for checking overlap and an auxiliary method for converting into AM.

With no restrictions

Edit: Assuming that each interval can be any length from 0 (inclusive) to 24 hours (exclusive) and may cross 00:00, this is somewhat more complicated, but I couldn’t let the challenge rest.

Some observations:

  1. If one interval is 12 hours or longer and the other has non-zero length, the two necessarily overlap.
  2. If both intervals are shorter than 12 hours, then if they do not overlap, we can go (count) cyclically forward from begin1 through end1 and begin2 to end2 in the order given here and either not cross 12 o’clock or cross 12 once and end up before begin1. If this cycle doesn’t work, then the intervals must overlap somehow.

In code:

public static boolean overlaps(LocalTime begin1, LocalTime end1, LocalTime begin2, LocalTime end2) {
    if (begin1.equals(end1)) { // zero length, cannot overlap anything
        return false;
    }
    if (begin2.equals(end2)) {
        return false;
    }

    // If any interval is 12 hours or longer,
    // the other one is necessarily included, that is, overlaps
    if (is12HoursOrLonger(begin1, end1)) {
        return true;
    }
    if (is12HoursOrLonger(begin2, end2)) {
        return true;
    }

    // Convert all times to AM
    begin1 = toAm(begin1);
    end1 = toAm(end1);
    begin2 = toAm(begin2);
    end2 = toAm(end2);

    // For the two intervals *not* to overlap we must be able to go forward
    // from begin1 through end1 and begin2 to end2 in this order either
    // not crossing 12 or crossing 12 once and ending before or on begin1
    boolean crossed12OClock = false;
    if (end1.isBefore(begin1)) { // to go forward to end1 we are crossing 12 o’clock
        crossed12OClock = true;
    }
    if (begin2.isBefore(end1)) {
        if (crossed12OClock) {
            // crossing 12 for the second time;
            // intervals cannot be in non-overlapping order
            return true;
        }
        crossed12OClock = true;
    }
    if (end2.isBefore(begin2)) {
        if (crossed12OClock) {
            return true;
        }
        crossed12OClock = true;
    }
    if (crossed12OClock) {
        return end2.isAfter(begin1);
    } else {
        return false;
    }
}

This method uses the following two auxiliary methods:

private static boolean is12HoursOrLonger(LocalTime begin, LocalTime end) {
    Duration length = Duration.between(begin, end);
    if (length.isNegative()) {
        length = length.plusDays(1);
    }
    return ! length.minusHours(12).isNegative();
}

private static LocalTime toAm(LocalTime time) {
    return time.with(ChronoField.AMPM_OF_DAY, 0);
}

Let’s try it out using the times from before:

    if (overlaps(begin1, end1, begin2, end2)) {
        System.out.println("They overlap");
    } else {
        System.out.println("They do not overlap");
    }

They overlap

Since the code and the arguments are complicated, make sure to cover the methods thoroughly with unit tests.

Upvotes: 1

Related Questions