Calendar functions

I have been reprogramming a clock to count the days as well as the hours. (If you are curious, this is the Alpha Clock Five from Evil Mad Science.)

I have decided to limit my calendar to the year range 2000 to 2099. This is because the Chronodot which came with the clock uses only a simple leap-year rule.

Here are some calendar functions I came up with.

days2greg seems to work pretty well
greg2days has not really been tested has been tested a little, and some bugs have been fixed
days2wknum is untested has not been very well tested

Any comments?

byte bin2bcd (byte x) { // useful conversion function
   return ((x/10)*16 + (x%10));
}

byte bcd2bin (byte x) { // another useful conversion function
   return ((x/16)*10 + (x%16)); 
}

unsigned long days2greg (unsigned int x) {
  // input to this function is a day number with day 0 = Friday 1 March 1996
  // output is 0x0WDDMMYY (ISO weekday number, then BCD date, BCD month, BCD year)
  // not lexically sortable but who cares, not for calculations anyway
  if ((x<MinValidDays) || (x>MaxValidDays)) return 0xDEADBEEF;
  byte w=5;
  w+=(x%7);
  if (w>7) w-=7; // this gives us our weekday
  byte y=0-4;
  byte m=3;
  byte d=1;
  y+=(4*(x/1461)); // 4-year intervals
  x%=1461;
  // we can't use a modulo operation for 0 to 3 years
  if (x>=730) {y+=2; x-=730;} // 2 years
  if (x>=365) {y++;  x-=365;} // 1 year
  if (x>=306) {y++; m=1; x-=306;} // 10 months: advance to January
  else if (x>=153) {m=8; x-=153;} // 5 months: advance to August
  if (x>=122) {m+=4; x-=122;} // 4 months
  if (x>=61) {m+=2; x-=61;} // 2 months
  if (x>=31) {m++; x-=31;} // 1 month
  // now we have the right year and month
  d+=x; // the day of the month
  // next, calculate output
  unsigned long r = w;
  r = (r<<8) + bin2bcd(d);
  r = (r<<8) + bin2bcd(m);
  r = (r<<8) + bin2bcd(y);
  return r;
}

unsigned int greg2days (unsigned long x) { // inverse of days2greg
  byte y; byte m; byte d; byte w;
  unsigned int q; unsigned int r; unsigned int s;
  y = x & 255;  x = x >> 8;
  m = x & 255;  x = x >> 8;
  d = x & 255;  x = x >> 8;
  w = x & 255;
  if (((y&15)>9) || ((m&15)>9) || ((d&15)>9)) return 50000;
  if ((w==0) || (w>7)) return 50000;
  y = bcd2bin(y);
  m = bcd2bin(m);
  d = bcd2bin(d);
  if ((y>99) || (m<1) || (m>12) || (d==0)) return 50000;
  switch (m) {
    case 4:
    case 11:
    case 9:
    case 6:
      if (d>30) return 50000;
      break;
    case 2:
      if (((y%4)>0) && (d>28)) return 50000;
      else if (d>29) return 50000;
      break;
    default:
      if (d>31) return 50000;
  }
  // 50000 = an error code
  // (year, month, day, and/or weekday is invalid or out of range)
  // next we do some math
  q = 45 + (y*12) + m; // count of months with month 0 = March 1996
  r = 1461*(q/48); q%=48; // 4-year intervals
  r += 365*(q/12); q%=12; // years
  r += 153*(q/5);  q%=5; // 5-month intervals
  r +=  61*(q/2);  q%=2; // 2-month intervals
  if (q) {r+=31; q--;} // one month
  r += (d-1); // days within the month
  // now r is a count of days with day 0 = Friday 1 March 1996
  s = 5 + (r%7); if (s>7) s-=7; // s is our expected day of the week
  if (s!=w) return 50001; // error code for unexpected day of the week
  return r;
}

byte days2wknum (unsigned int x){
  // input to this function is a day number with day 0 = Friday 1 March 1996
  // output is week number in BCD format
  // ISO week numbering is used
  if ((x<MinValidDays) || (x>MaxValidDays)) return 0xEE;
  x -= 306; // move start date to 1997-01-01 (Wed)
  unsigned int z = (x+2) % 7; // days since most recent Monday (zero-based)
  x = x - z + 3; // round to nearest Thursday
  x %= 1461; // get rid of 4-year intervals
  if (x >= 730) x -= 730; // 2 years
  if (x >= 365) x -= 365; // 1 year
  // now x is (zero-based) days since start of year
  byte w = (x / 7) + 1; // ISO week number
  return bin2bcd(w);
}