It seems you are assuming that military hours are always 4 digits with an appropriate number of leading zeroes, from 0000 through 2359. You can’t represent this concept of military hours in an int since an int hasn’t necessarily got 4 digits. The same number can be formatted into 6, 06, 006, 0006 or 000000006. Internally it’s just the same 32 bits. So when firstTime
is 700 (or 0700), then Integer.toString(firstTime).substring(0, 2))
takes the first two digits of 700 and yields 70 (not 7). So new TimeInterval(700, 1400).getHours()
yields -56 (not 7). If firstTime
is 9 (0009) or less, your code will probably crash with a StringIndexOutOfBoundsException
.
A solution would be to pass your military hours in a String
that always has length 4. Then your substring operation will always take the first 2 digits, the hours. If you insist on fighting your way through with int
, another solution would be a modulo 100 operation to get the hours: firstTime % 100
.
You mentioned base 8, also known as octal. There is nothing in the code you have shown that would cause base 8 to be used. Of course, if you use your class like new TimeInterval(0700, 1400)
, then Java will take 0700 to be base 8, so 448, you are correct. In this case the hours you get will be 14 - 44 = -30. Again passing a string will solve it.
Digging a bit deeper and finding a good solution
I would further like to suggest:
- When the times are 1059 and 1202, there are 1 hour 3 minutes between them. Don’t you want the hours to be 1 rather then 2 in this case?
- Use the
LocalTime
class of java.time, the modern Java date and time API, for your time of day. When you use military hours in some interface, keep them in the interface, no matter if in String
or int
form or both. Do the proper conversion when building your TimeInterval
instance.
So your class may become:
public class TimeInterval{
private static final DateTimeFormatter FORMATTER_FOR_MILITARY_HOURS
= DateTimeFormatter.ofPattern("HHmm");
private LocalTime firstTime;
private LocalTime secondTime;
/** Main constructor */
public TimeInterval(LocalTime firstTime, LocalTime secondTime){
if (firstTime.isAfter(secondTime)) {
// Switch around
this.firstTime = secondTime;
this.secondTime = firstTime;
} else {
this.firstTime = firstTime;
this.secondTime = secondTime;
}
}
/** Convenience constructor accepting military hours */
public TimeInterval(String first, String last) {
this(LocalTime.parse(first, FORMATTER_FOR_MILITARY_HOURS),
LocalTime.parse(last, FORMATTER_FOR_MILITARY_HOURS));
}
public int getHours(){
long differenceInHours = ChronoUnit.HOURS.between(firstTime, secondTime);
return Math.toIntExact(differenceInHours);
}
}
This also gives us a range check for free: LocalTime
only handles time from 00:00 to 23:59:59.999999999. So passing time soutside this range will throw an exception (sorry, 2400 cannot be handled). The same will happen if the string is not in the correct format, like when the length isn’t 4 or the minutes are greater than 59.
The this(
…)
in the convenience constructor is a call to the other constructor, the one accepting LocalTIme
as arguments.
ChronoUnit.between()
returns a long. Since we know that there can be 23 hours at most, we can safely convert to int
. Math.toIntExact()
does that for us.