Via Twitter on the weekend I came across this pastie (see lines 259-274), supposedly containing the code for the Real-Time-Clock running on the 30GB Zune.
As you can see in the code an infinite loop occurs as soon as the local variable ‘days’ is exactly 366 and ‘year’ is a leap year. This was the case on January 1, 2009 GMT and is therefore aptly known as a Y2K9 bug. The glitch resulted in headlines such as:

This could have been prevented had the code coverage report been inspected 😉
I converted the buggy function to Java:

final int ORIGINYEAR = 1980;
/**
Function: ConvertDays
Local helper function that split total days since Jan 1,
ORIGINYEAR into year, month and day
Parameters:
Returns:
Returns TRUE if successful, otherwise returns FALSE.
*/
boolean ConvertDays(int days, Date lpTime) {
int dayofweek, month, year;
int month_tab;
year = ORIGINYEAR;
while (days > 365) {
if (IsLeapYear(year)) {
if (days > 366) {
days -= 366;
year += 1;
}
} else {
days -= 365;
year += 1;
}
}
return true;
}

and wrote the following test case:

public void testConvertDays() {
final RTC rtc = new RTC();
final ReadableInstant originDate = new DateTime(rtc.ORIGINYEAR, 1, 1, 0, 0, 0, 0);
// test on 2008, 12, 31.
final ReadableInstant date20081231 = new DateTime(2008, 12, 31, 0, 0, 0, 0);
assertTrue(rtc.ConvertDays(Days.daysBetween(originDate, date20081231).getDays(), new Date(date20081231.getMillis())));
}

Running the test with Clover enabled shows the following:

The if (days > 366) branch never evaluates to false. Adding the next test for this case, causes the infinite loop that Zune users experienced on Y2K9:

final ReadableInstant date20090102 = new DateTime(2009, 1, 1, 0, 0, 0, 0);
rtc.ConvertDays(Days.daysBetween(originDate, date20090102).getDays(), new Date(date20090102.getMillis()));

And in the interest of fair and balance reporting, don’t forget that the iPod was born in an infinite loop…

Work smarter, better, and faster with weekly tips and how-tos.

Subscribe now