The failing test cases:
src/test/java/org/joda/time/TestDateTimeZoneCutover.java:1249-1263
public void testBug3476684_adjustOffset() {
final DateTimeZone zone = DateTimeZone.forID("America/Sao_Paulo");
DateTime base = new DateTime(2012, 2, 25, 22, 15, zone);
DateTime baseBefore = base.plusHours(1); // 23:15 (first)
DateTime baseAfter = base.plusHours(2); // 23:15 (second)
assertSame(base, base.withEarlierOffsetAtOverlap());
assertSame(base, base.withLaterOffsetAtOverlap());
assertSame(baseBefore, baseBefore.withEarlierOffsetAtOverlap());
assertEquals(baseAfter, baseBefore.withLaterOffsetAtOverlap()); // <-- TC fails here
assertSame(baseAfter, baseAfter.withLaterOffsetAtOverlap());
assertEquals(baseBefore, baseAfter.withEarlierOffsetAtOverlap());
}
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:1163-1180
public long adjustOffset(long instant, boolean earlierOrLater) {
// a bit messy, but will work in all non-pathological cases
// evaluate 3 hours before and after to work out if anything is happening
long instantBefore = convertUTCToLocal(instant - 3 * DateTimeConstants.MILLIS_PER_HOUR); //<-- BUG!!
long instantAfter = convertUTCToLocal(instant + 3 * DateTimeConstants.MILLIS_PER_HOUR); //<-- BUG!!
if (instantBefore == instantAfter) { //<-- BUG!!
return instant; // not an overlap (less than is a gap, equal is normal case)
}
// work out range of instants that have duplicate local times
long local = convertUTCToLocal(instant); //<-- BUG!!
return convertLocalToUTC(local, false, earlierOrLater ? instantAfter : instantBefore); //<-- BUG!!
// calculate result
// currently in later offset
// currently in earlier offset
}
Other code:
src/main/java/org/joda/time/DateTime.java:664-667
public DateTime withEarlierOffsetAtOverlap() {
long newMillis = getZone().adjustOffset(getMillis(), false);
return withMillis(newMillis);
}
src/main/java/org/joda/time/DateTime.java:684-687
public DateTime withLaterOffsetAtOverlap() {
long newMillis = getZone().adjustOffset(getMillis(), true);
return withMillis(newMillis);
}