The failing test case:
src/test/java/org/joda/time/TestDateTimeZone.java:303-334
public void testForOffsetHoursMinutes_int_int() {
assertEquals(DateTimeZone.UTC, DateTimeZone.forOffsetHoursMinutes(0, 0));
assertEquals(DateTimeZone.forID("+23:59"), DateTimeZone.forOffsetHoursMinutes(23, 59));
assertEquals(DateTimeZone.forID("+03:15"), DateTimeZone.forOffsetHoursMinutes(3, 15));
assertEquals(DateTimeZone.forID("-02:00"), DateTimeZone.forOffsetHoursMinutes(-2, 0));
assertEquals(DateTimeZone.forID("-02:30"), DateTimeZone.forOffsetHoursMinutes(-2, 30));
assertEquals(DateTimeZone.forID("-23:59"), DateTimeZone.forOffsetHoursMinutes(-23, 59));
try {
DateTimeZone.forOffsetHoursMinutes(2, 60);
fail();
} catch (IllegalArgumentException ex) {}
try {
DateTimeZone.forOffsetHoursMinutes(-2, 60);
fail();
} catch (IllegalArgumentException ex) {}
try {
DateTimeZone.forOffsetHoursMinutes(2, -1);
fail();
} catch (IllegalArgumentException ex) {}
try {
DateTimeZone.forOffsetHoursMinutes(-2, -1);
fail();
} catch (IllegalArgumentException ex) {}
try {
DateTimeZone.forOffsetHoursMinutes(24, 0); // <-- wrong behaviour
fail(); // <-- TC fails here
} catch (IllegalArgumentException ex) {}
try {
DateTimeZone.forOffsetHoursMinutes(-24, 0);
fail();
} catch (IllegalArgumentException ex) {}
}
The faulty code:
Line is covered but NOT in slice
Line is covered and in slice
Line is in slice but NOT covered
src/main/java/org/joda/time/DateTimeZone.java:254-274
public static DateTimeZone forOffsetHoursMinutes(int hoursOffset, int minutesOffset) throws IllegalArgumentException {
if (hoursOffset == 0 && minutesOffset == 0) {
return DateTimeZone.UTC;
}
if (minutesOffset < 0 || minutesOffset > 59) {
throw new IllegalArgumentException("Minutes out of range: " + minutesOffset);
}
int offset = 0;
try {
int hoursInMinutes = FieldUtils.safeMultiply(hoursOffset, 60); //<-- BUG!!
if (hoursInMinutes < 0) {
minutesOffset = FieldUtils.safeAdd(hoursInMinutes, -minutesOffset); //<-- BUG!!
} else {
minutesOffset = FieldUtils.safeAdd(hoursInMinutes, minutesOffset); //<-- BUG!!
}
offset = FieldUtils.safeMultiply(minutesOffset, DateTimeConstants.MILLIS_PER_MINUTE);
} catch (ArithmeticException ex) {
throw new IllegalArgumentException("Offset is too large");
}
return forOffsetMillis(offset);
}
Other code:
src/main/java/org/joda/time/DateTimeZone.java:602-620
private static int parseOffset(String str) {
// Can't use a real chronology if called during class
// initialization. Offset parser doesn't need it anyhow.
Chronology chrono = new BaseChronology() {
public DateTimeZone getZone() {
return null;
}
public Chronology withUTC() {
return this;
}
public Chronology withZone(DateTimeZone zone) {
return this;
}
public String toString() {
return getClass().getName();
}
};
return -(int) offsetFormatter().withChronology(chrono).parseMillis(str);
}