Oscar van Vlijmen <o.van.vlijmen@tip.nl> writes:
Allow me to give a couple of key sources for Zeller's algorithm to find the day of the week:
I see that those source's formulas have diverged a bit from Zeller's original algorithm, which you can find in: Chr. Zeller, Kalender-Formeln, Acta Mathematica, Vol. 9, Nov. 1886 I haven't read the original article (I don't read German), but I have seen a translation whose location escapes me now. I think if you translate his original formulas unchanged into C, and assume nonnegative years, they would look like this: /* Calculcate weekday number for Gregorian date YYYY-MM-DD. */ int jan_or_feb = MM <= 2; int y = YYYY - jan_or_feb; int J = y / 100; // century number int K = y % 100; // year number within the same int m = MM + 12 * jan_or_feb; // month number int q = DD; // day number in the month int h = (q + ((m + 1) * 26) / 10 + K + K/4 + J/4 - 2*J) % 7; // weekday number (1 is Sunday) However, this ignores overflows and doesn't handle negative years correctly. If you want it to work more generally, you need to alter the formulas, e.g. to something like the following. Please note that I've written this code by hand just now without testing it, so it's quite possibly buggy. #define DIV(a, b) (((a)/(b)) - ((a)%(b) < 0)) int jan_or_feb = MM <= 2; int K1 = YYYY % 100; int K2 = K1 - (K1 == 0 & jan_or_feb); int J = YYYY / 100 - (K2 < 0); int K = (K2 + 100) % 100; int m = MM + 12 * jan_or_feb; int q = DD; int h1 = (q + ((m + 1) * 26) / 10 + K + DIV(K,4) + DIV(J,4) - 2*J) % 7; int h = (h1 + 6) % 7;
The following C code is believed to work correctly for all reasonable cases (if it does not work in 1000 years - sorry - dig me up and I will fix it) and is copied directly from our software and was written following the Zeller algorithm described in a book written by Terry Dollhoff back in 1980 (I belive that the title was "16 Bit Microprocessor Architecture" and publisher was Reston Publishing but if anybody needs exact information, I will find the book at home). the book gave this as an example for doing fixed point arithmetic (since floating point was not usually available in the micro world back then) and I modified it to be all integer instead. The "DTB" structure declaration is not included but should be of a pretty obvious format based on the field names. /*----------------------------------------------------------------------*/ /* set_day_of_week - Set day of week from valid DTB. */ /*----------------------------------------------------------------------*/ /* Zellers Congruence Data automatically calculate the day of the week from the entered date. */ static int zmonth_base[12] = {2,5,0,3,5,1,4,6,2,4,0,3}; void set_day_of_week(DTB *dtb) { int zmonth; int zyear; int zcentury; zmonth=dtb->month-2-1; /* march=0 for this formula */ if(zmonth<0) { zmonth+=12; zyear=(dtb->year-1)%100; zcentury=(dtb->year-1)/100; } else { zyear=dtb->year%100; zcentury=dtb->year/100; } /* The Zellers Congruence returns a zero based number for the day of the week. */ dtb->wkday = zmonth_base[zmonth] + dtb->day + zyear + (zyear/4) - 2*zcentury+(zcentury/4); dtb->wkday= (dtb->wkday%7) + 1; /* dtb.wkday is one based (1=sunday) */ if(dtb->wkday<=0) dtb->wkday+=7; } The last line in the routine was added because this implementation failed to handle Saturday correctly starting in March this year (because the wkday field is 1 based). this may be why several examples I have seen add 77 (or othersome other multiple of 7) in the calculation because the wkday field would otherwise become negative on Saturday which messes things up a bit. -- Martin Smoot Network Storage Solutions 703-834-2242 msmoot@nssolutions.com www.nssolutions.com
participants (2)
-
Martin Smoot -
Paul Eggert