Reputation: 9152
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
Reputation: 86324
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.
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:
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