Microsoft Issues Workaround For Zune Freeze
UnknowingFool writes "As a followup to the Zune New Year's Eve meltdown, Microsoft has issued a workaround for what some users have correctly guessed was a bug caused by a leap year. To recover from the problem, let the Zune drain the batteries and restart it after noon on January 1, 2009. Many sites are reporting that Microsoft has 'fixed' the issue, but technically all Microsoft has done is to ask users to wait out the conditions that triggered the bug. Unless a software patch comes out, Zunes will suffer the same problem again in four years." Reader ndtechnologies adds, "According to posts in the Toshiba forum at anythingbutipod.com, the same bug that shut down millions of Zune 30's also affects the Toshiba Gigabeat S. The Zune 30 is based off of the Gigabeat S series and was co-developed by Microsoft with Toshiba."
So this is an acceptable solution.
if (year % 400 == 0) hard_crash()
else if (year % 100 == 0) boot()
else if (year % 4 == 0) hard_crash()
else boot()
Here is source of the trouble.
Here's the actual buggy code.
The error is infinite loop in ConvertDays(), starting at line 249. The first loop does not cope with "IsLeapYear() == true" when "days == 366"
while (days > 365)
{
if (IsLeapYear(year))
{
if (days > 366)
{
days -= 366;
year += 1;
}
}
else
{
days -= 365;
year += 1;
}
}
source code that caused the bug.
The first step to fix any microsoft problem - reboot.
I'm sorry, I can't hear you over the sound of how awesome I am.
In case you can't see how this fails: On December 31st in a leap year, days is counted down to 366 like it's supposed to, and then the IsLeapYear() test is true, but days>366 is not, so the loop body does nothing and the while becomes an infinite loop.
This code can not possibly have been accepted in any kind of code review. Someone would have pointed out that there are O(1) formulas for calendar calculations.
Wouldn't it be faster for Microsoft to simply give each of the 8 users a call and walk them through the work-around? If their numbers change in the next four years, they can simply notify Zune support.
I hadn't turned on the Zune before I read about the problem, so I just left it off and turned it on the morning of Jan 1 and everything worked fine, no need to drain battery or anything.
The above is not an admission that I own a Zune, just what I theoretically would have done and the theoretical results, based on heavy pretend observations.
Typical MS. They're copying Apple again but this time too literally. :P
Well, there's spam egg sausage and spam, that's not got much spam in it.
The function iteratively converts days into years. If there are more days left in the days variable than there are days in a year (365), then the days in that year are subtracted from the days variable and added as 1 year to the year variable.
It starts with year=1980, because the RTC counts days since 1/1/1980 (inclusive). For normal years, it subtracts 365 days and adds a year. After the first iteration, year is 1981 and days is the number of days since 1/1/1981 (inclusive). If the loop is currently looking at a leap year, then 366 days are subtracted from the days variable and 1 year is added to the year variable. So far the code looks like this:
while (days>365){
if (IsLeapYear(year)){
days-=366;
year+=1;
}else{
days-=365;
year+=1;
}
}
The crazy thing is that someone must have looked at the edge case in that code: The last day of a leap year. On that day, the above code fails by incrementing year once too often, because at the beginning of the last iteration, the loop test indicates that there are more days in the days variable than there are in a year, but there are not, because a leap year has 366 days. That edge case is caught by the second if:
while (days>365){
if (IsLeapYear(year)){
if (days>366){
days-=366;
year+=1;
}
}else{
days-=365;
year+=1;
}
}
The loop goes into the last iteration because of the loop condition which is on day short, then the IsLeapYear test selects the first branch, and instead of blindly incrementing the year and subtracting 366 days, there is an extra check if there are really more days left to make a full year...and then it fails to handle the only case where the code fails without that extra check. It should simply break the loop:
while (days>365){ // days==366 && IsLeapYear(year): end of calculation
if (IsLeapYear(year)){
if (days>366){
days-=366;
}else{
break;
}
}else{
days-=365;
}
year+=1;
}
(Matt's code always reduces the day count by 366. 1980 was a leap year.)