LocalTime between 23.59 and 00:01 [closed]
Asked Answered
A

4

16

I want to check if a LocalTime is midnight. For this use case midnight is defined as anything in between 23:59 and 00:01. That is a range of 2 minutes.

private final LocalTime ONE_MINUTE_BEFORE_MIDNIGHT = LocalTime.of(23, 59, 0);
private final LocalTime ONE_MINUTE_AFTER_MIDNIGHT = LocalTime.of(0, 1, 0);

I have a method

public boolean isAtMidnight(LocalTime time) {
    return time.isAfter(ONE_MINUTE_BEFORE_MIDNIGHT)
        && time.isBefore(ONE_MINUTE_AFTER_MIDNIGHT);
}

This method is always returning false. Even for LocalTime.MIDNIGHT. it should, however, return true.

How can I check if the time is +-1 minute from midnight?

Armelda answered 23/3, 2018 at 13:54 Comment(4)
Hint: are you sure you want it to be after 23:59 and before 00:01?Villa
suggestion: the limit variables are more clearly defined as private final LocalTime ONE_MINUTE_BEFORE_MIDNIGHT = LocalTime.MIDNIGHT.minusMinutes(1); and vide versaHenchman
Of course, if you wanted to check for noon +/- 1 min, you'd need to test for "after 11:59 and before 12:01"...Jurisprudence
Out of curiosity, why is 23.59.01 considered midnight?Choice
M
17

Instead of checking if time is after 23:59 and before 00:01, you should check if it is after 23:59 or before 00:01.

public boolean isAtMidnight(LocalTime time){
    return time.isAfter(ONE_MINUTE_BEFORE_MIDNIGHT) || time.isBefore(ONE_MINUTE_AFTER_MIDNIGHT);
}

If we look at the implementation for LocalTime#isAfter, we see the following:

public boolean isAfter(LocalTime other) {
    return compareTo(other) > 0;
}

Looking at LocalTime#compareTo:

@Override
public int compareTo(LocalTime other) {
    int cmp = Integer.compare(hour, other.hour);
    if (cmp == 0) {
        cmp = Integer.compare(minute, other.minute);
        if (cmp == 0) {
            cmp = Integer.compare(second, other.second);
            if (cmp == 0) {
                cmp = Integer.compare(nano, other.nano);
            }
        }
    }
    return cmp;
}

We can see that two instances of LocalTime are first compared by their respective hours, then minutes, then seconds, and finally nanoseconds. For LocalTime#compareTo to return a value greater than 0 to satisfy LocalTime#isAfter, the hour of the first LocalTime instance must be greater than the second instance's. This is not true for 00:00 and 23:59, hence why your method returns false. The same analysis can be done for LocalTime#isBefore, and you'll arrive at the same result.

Keep in mind that you can just check against LocalTime.MIDNIGHT if you want to be exact, but I assume you're considering any time within a 1-minute range to be "midnight" (including seconds).

Micrometeorology answered 23/3, 2018 at 14:0 Comment(0)
S
18

The solution is to use || instead of &&:

public boolean isAtMidnight(LocalTime time) {
    return time.isAfter(ONE_MINUTE_BEFORE_MIDNIGHT) || time.isBefore(ONE_MINUTE_AFTER_MIDNIGHT);
}

This is so counter-intuitive, isn't it? The trick is that 00:00:01 are not after 23:59, so that's always going to fail. This is so because LocalTime.isAfter or LocalTime.isBefore assumes that those are times of the same day.

Senhor answered 23/3, 2018 at 14:13 Comment(3)
thanks for the explanationLajuanalake
This is a much better answer than the highest upvoted one.Kandykane
@MrLister I respectfully disagree, mainly because LocalTime has no concept of a day.Micrometeorology
M
17

Instead of checking if time is after 23:59 and before 00:01, you should check if it is after 23:59 or before 00:01.

public boolean isAtMidnight(LocalTime time){
    return time.isAfter(ONE_MINUTE_BEFORE_MIDNIGHT) || time.isBefore(ONE_MINUTE_AFTER_MIDNIGHT);
}

If we look at the implementation for LocalTime#isAfter, we see the following:

public boolean isAfter(LocalTime other) {
    return compareTo(other) > 0;
}

Looking at LocalTime#compareTo:

@Override
public int compareTo(LocalTime other) {
    int cmp = Integer.compare(hour, other.hour);
    if (cmp == 0) {
        cmp = Integer.compare(minute, other.minute);
        if (cmp == 0) {
            cmp = Integer.compare(second, other.second);
            if (cmp == 0) {
                cmp = Integer.compare(nano, other.nano);
            }
        }
    }
    return cmp;
}

We can see that two instances of LocalTime are first compared by their respective hours, then minutes, then seconds, and finally nanoseconds. For LocalTime#compareTo to return a value greater than 0 to satisfy LocalTime#isAfter, the hour of the first LocalTime instance must be greater than the second instance's. This is not true for 00:00 and 23:59, hence why your method returns false. The same analysis can be done for LocalTime#isBefore, and you'll arrive at the same result.

Keep in mind that you can just check against LocalTime.MIDNIGHT if you want to be exact, but I assume you're considering any time within a 1-minute range to be "midnight" (including seconds).

Micrometeorology answered 23/3, 2018 at 14:0 Comment(0)
C
3

If you really need to do this that way, it cannot fulfil both statements. Consider using or, this code returns true for me:

public static void main(String[] args)
{
    LocalTime ONE_MINUTE_BEFORE_MIDNIGHT = LocalTime.of(23, 59, 0);
    LocalTime ONE_MINUTE_AFTER_MIDNIGHT = LocalTime.of(0, 1, 0);
    LocalTime md = LocalTime.MIDNIGHT;
    System.out.println(md.isBefore(ONE_MINUTE_AFTER_MIDNIGHT) || md.isAfter(ONE_MINUTE_BEFORE_MIDNIGHT));
}
Communicant answered 23/3, 2018 at 14:0 Comment(0)
S
3

As already explained, you have to use || instead of &&. I think is more readable if you reorganize the variables in the condition this way:

public boolean isAtMidnight(LocalTime time) {
    return ONE_MINUTE_BEFORE_MIDNIGHT.isBefore(time) || ONE_MINUTE_AFTER_MIDNIGHT.isAfter(time);
}

One minute before midnight is before...

Sheepcote answered 23/3, 2018 at 14:36 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.