Cause of ZUNE leapyear problem - Freescale date routine

itsnotabigtruck writes -

After doing some poking around in the source code for the Zune's clock driver (available free from the Freescale website), I found the root cause of the now-infamous Zune 30 leapyear issue that struck everyone on New Year's Eve.


The Zune's real-time clock stores the time in terms of days and seconds since January 1st, 1980. When the Zune's clock is accessed, the driver turns the number of days into years/months/days and the number of seconds into hours/minutes/seconds. Likewise, when the clock is set, the driver does the opposite.

The Zune frontend first accesses the clock toward the end of the boot sequence. Doing this triggers the code that reads the clock and converts it to a date and time. Below is the part of this code that determines the year component of the date:



year = ORIGINYEAR; /* = 1980 */

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


Looks like it's a leap year thing and it might happen again in 4 years... For now those with ZUNEs can just wait a day. This bug and the android "run every word you type" bug - typing "reboot" would reboot the phone are 2008's weirdest mobile device bugs.





Related:

Recent Entries

Comments

Oldest comments listed first.

Posted by: maushammer on December 31, 2008 at 6:45 PM

Awesome find!

It looks like today is day 366, so the inner test ("if (days > 366)") knows enough not to increment the year yet, but the outer loop still thinks the year isn't done ("days > 365"). While loops can get tricky! (and by tricky, I mean fail in a bad way).

The code has two copyrights, so it looks like Microsoft modified Freescale's original code. I wonder who made the mistake, and if anyone else uses this code...

Thanks!


Posted by: Skydiveguy on December 31, 2008 at 7:07 PM

Zune = Fail

In 4 years... all the current Zune owners will have upgraded to an iPod.


Posted by: Patrick on January 1, 2009 at 12:31 AM

Well THERE'S your problem

while (days > 365 && !(IsLeapYear(year) && days == 366))
...

That's kind of a hack I guess, can anyone think of a better way?


Posted by: Thomas on January 1, 2009 at 1:09 AM

the most explicit way to formulate the expression is, because it says, what the case for leap years and the case for regular years is:
while ( (days > 365 && !IsLeapYear(year)) || (days > 366 && IsLeapYear(year)))

to avoid calling IsLeapYear twice, it could be shortened to (has to be at least 366 days left, unless it is regular year, then 366 are okay to go to the next year)
while ((days > 366 || (IsLeapYear(year) && days == 366))

The little advantage is that at runtime, only the first condition days > 366 will be tested most of the time. So, IsLeapYear doesn't have to be checked.

How about (for concept, might have some bugs):
year = days / 366 + ORIGINYEAR;
days = days % 366;
leapYearDays = (year - ORIGINYEAR) / 4 + 1;
days += leapYearDays;
if ((days > 366 || (IsLeapYear(year) && days == 366)) {
days -= (IsLeapYear(year) ? 366 : 365;
year++;
}

Obviously, there is a bug every 100 years, because there are special leap year rules. Though, one more if-statement for that. And, those devices probably won't be around long enough to run into special leap year case.


Posted by: Thomas on January 1, 2009 at 1:11 AM

year = days / 366 + ORIGINYEAR;
days = days % 366;
leapYearDays = (year - ORIGINYEAR) / 4 + 1;
days -= leapYearDays;
if ((days > 366 || (IsLeapYear(year) && days == 366)) {
days -= (IsLeapYear(year) ? 366 : 365;
year++;
}


Posted by: Thomas on January 1, 2009 at 1:15 AM

Jeez, it's too late in the night. Though, you get the idea to use modulo and the knowledge that every four years is a leap year (mostly). That way no loop is needed.

I wasn't sure, if it was better to add the extra days or subtract them. I kept going back and force. That's how the errors ended up, because I had pieces left from either solution.

year = days / 366 + ORIGINYEAR;
days = days % 366;
leapYearDays = (year - ORIGINYEAR) / 4 + 1;
days -= leapYearDays;
if (days < 0) {
year--;
days += (IsLeapYear(year) ? 366 : 365;
}


Posted by: Gamma Goblin on January 1, 2009 at 1:56 AM

gammagoblin.blogspot.com

...
if (days > 366)
{
days -= 366;
year += 1;
}
else
{
break;
}
...


:-D


Posted by: Anonymous on January 1, 2009 at 11:40 PM

yeah....just add a break


Posted by: Bebbo on January 3, 2009 at 10:45 PM

coders code too much

ommitting errors fixes it:

year = ORIGINYEAR; /* = 1980 */

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

less is more!

Bebbo


Posted by: Morkan on January 4, 2009 at 7:09 AM

Still to complicated ...

.. and wouldn't work either, because:
while (days > 365) would execute on 31.12.2012 and it would be a leap year, but it would still be 2012, so
days -= 366 and year += 1 mustn't be executed.

Every loop will stay wrong as long as the exit condition is static.

This will work and is much less complicated:

int daysayear = (365 + (isLeapYear(year)?1:0) )
while (days > daysayear)
{
days -= daysayear;
year += 1;
daysayear = (365 + (isLeapYear(year)?1:0) )
}


Posted by: Torben Deumert on January 4, 2009 at 12:55 PM

@Morkan: works, but...

you version is correct, but the fact that the first line appears two times is bugging me.

I would suggest an own function for daysAYear:

int daysAYear(int year) {
return 365 + (isLeapYear(year)?1:0);
}

then the code can be like this:

int year = ORIGINYEAR;
while (days > daysAYear(year)) {
days -= daysAYear(year);
year++;
};

The additional processing overhead should be tolerable. The main problem cannot be solved: the code was obviously not tested; or at least not well tested. A simple boundary value test would have saved the Zune users a lot of trouble. On the other hand: it's a pretty bad, if code for processing the clock has the possibility to hang the whole device. Just another example of the quality of software code offered nowadays.


Posted by: Gp on January 5, 2009 at 1:57 PM

Why are still figuring out how to design a leap-year calculator?

Wasn't this problem solved like 20 years ago?


Posted by: Pete on January 5, 2009 at 4:09 PM

Why...

Why is this a while loop?

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


Posted by: Gp on January 6, 2009 at 5:59 AM

Anyone wondering...

Why we are still inventing code for leap year calculations? Wasn't this problem solved 20 years ago?


Leave a comment


Subscribe to MAKE!Subscribe to MAKE Magazine!

Subscribe today, save 42% and get web access to MAKE free. MAKE Digital Edition is available only to subscribers.

$34.95 / 1 year
(4 Quarterly Issues)

Subscribe now

How-to videos for Makers and Crafers!


Void your warranty, violate a user agreement, fry a circuit, blow a fuse, poke an eye out. Make: The risk-takers, the doers, the makers of things... Welcome to Make: Online!


CRAFT Maker Shed Maker Faire MAKE television
MAKE: en EspaƱol MAKE: Japan

Make: television
Make: television is here! Visit makezine.tv or iTunes to see all the episodes.

Connect with MAKE

Be a MAKE fan on Facebook MAKE on Facebook
Visit our Facebook page and become a fan of MAKE!
MAKE on Twitter MAKE on Twitter
Follow our MAKE tweets!
MAKE Flickr Pool MAKE on Flickr
Join our MAKE Flickr Pool!
    make_tips on Twitter

    MAKE's RSS feed is here.
    Add MAKE to iGoogle - GoogleGoogle.
    How to add MAKE to your RSS reader - Real simple.
    Add MAKE on FriendFeed


    Advertise here with FM.

    Why advertise on MAKE?
    Read what folks are saying about us!

    Click here to advertise on MAKE!

    MAKE wins Treehugger Best of Green Award

    MAKE wins Brit Insurance Design Award

    Subscribe to MAKE Magazine!

    Make: Online authors!

    Gareth Branwyn, Chris Connors (guest author), Collin Cunningham, Marc de Vinck, Peter Horvath (intern), Kip Kay, Goli Mohammadi, John Park, Sean Ragan, Becky Stern, Phillip Torrone

    Suggest a Site!

    Current Podcast

    itunesdl.gif Weekend Project: Fire Piston Make your own fire starter that uses compressed air and burns at 500 degrees! Thanks to Bill Gurstelle for showing us this at Maker Faire. To download The Fire Piston MP4 click here or subscribe in iTunes. Pick up... More...

    Get the Make: Online sent via email
    Enter your email to receive Make: Online each day:






    Subscribe to MAKE Magazine!

    Recent Posts from the Craft: Blog