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.




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


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




Check out more videos from MAKE.

Maker SHED

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 Archives

    Make: Money

    Make: Science Room
    Subscribe to MAKE Magazine!

    Make: Online editors and authors!

    Gareth BranwynGareth Branwyn
    Editor-in-Chief


    Phillip TorronePhillip Torrone
    Senior Editor
    | Web | Twitter


    Becky SternBecky Stern
    Associate Editor
    | AIM | Twitter


    Marc de VinckMarc de Vinck
    Contributing Writer
    | AIM | Twitter


    John ParkJohn Park
    Contributing Writer
    | Twitter


    Sean RaganSean Ragan
    Contributing Writer
    | Twitter


    Matt MetsMatt Mets
    Contributing Writer
    | AIM | Twitter


    Dale DoughertyDale Dougherty
    Editor & Publisher
    | Twitter


    Shawn ConnallyShawn Connally
    Managing Editor
    | Twitter


    Goli MohammadiGoli Mohammadi
    Associate Managing Editor

    Kip KayKip Kay
    Weekend Projects
    | AIM | Twitter


    Collin CunninghamCollin Cunningham
    Contributing Writer
    | AIM | Twitter

    Adam FlahertyAdam Flaherty
    Contributing Writer
    | AIM | Twitter


    John BaichtalJohn Baichtal
    Contributing Writer
    | AIM | Twitter



    More contributors: Mark Frauenfelder (Editor-in-Chief, MAKE magazine), Kipp Bradford (Technical Consultant/Writer), Chris Connors (Education), Diana Eng (Guest Author), Peter Horvath (Intern), Brian Jepson (O'Reilly Media), Robert Bruce Thompson (Science Room)

    Suggest a Site!

    Advertise here with FM.

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

    Click here to advertise on MAKE!



    Current Podcast

    itunesdl.gif Behind the Scenes at MAKE and CRAFT In January, many of the remote MAKE/CRAFT team members (myself included) convened at the Maker Media headquarters at O'Reilly Media in Sebastopol, California. Take a look behind the scenes of your favorite DIY publications as Goli Mohammadi gives us... More...

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



    Sign up for the Make: Newsletter

    Our Make: Newsletter covers news from maker Media, has original columns, Shed deals, and more! You can also read the archives of past issues.


     



    MAKE Fascination video series brought to you by Dow

    Make: Education
    MAKE: en EspaƱol MAKE: Japan
    Important please read


    Subscribe to MAKE Magazine!

    Recent Posts from the Craft: Blog