So yesterday, on December 31, 2008, everyone with one of the original 30GB Zunes (I have two) found their precious portable media players frozen at a startup screen. Of course, in today’s blogging world, this hit the news quickly and Microsoft found themselves swamped with support calls and demands to fix the problem (wouldn’t that be a great way to wake up?).

This morning, I saw that a user over at ZuneBoards.com has found the bug in a reverse-engineered version of the Zune 30 source code. This is just such a classic programming “oops” that I had to post it for other geeks to enjoy. Here’s the code:

year = ORIGINYEAR; /* = 1980 */

while (days > 365)
{
if (IsLeapYear(year))
{
if (days > 366)
{
days -= 366;
year += 1;
}
}
else
{
days -= 365;
year += 1;
}
}

So let’s walk through what it’s doing. The Zune stores its date as the number of days past January 1, 1980. When it first starts up, it has this value and needs to calculate the current year. Off it goes through a loop. In each cycle of the loop, it’s going to first check to see if there are more than 365 days left. If so, it will be able to increment the year value, take away a year worth of days, and continue on the loop. The little twist is that it needs to check to see whether the year is a leap year. If so, it’s going to have to subtract 366 days instead of subtracting 365 days. So far, what I’ve described sounds great. The problem is the implementation in the code.

Let’s imagine that this code ran on December 31, 2008. It would cycle through the loop 28 times, leaving year at 2008 and days at 366 (since it is the 366th day of the year). So this would pass the top condition of the loop (days > 365) and enter the inner loop. Since 2008 is a leap year, it would next check if days > 366, which would be false (i.e. it doesn’t need to add another year). But now it would go out to the top of the loop and since days is still > 365, it would run through the loop again, and again, and again… There are obvious ways to fix this, such as breaking out of the main loop if it is a leap year and days = 366, or adding some complexity to the main loop to say something like while (IsLeapYear(year) && days > 366) || (!IsLeapYear(year) && days > 365).

I do feel sorry for whoever wrote this code, and I bet that he/she will NEVER live it down! 🙂