Request for simple date routines.

Ian Collier imc at prg.ox.ac.uk
Sat Apr 20 02:52:34 AEST 1991


I don't know whether you've had an answer or not (I haven't read one
in this newsgroup), so...

In article <1991Apr9.234255.143 at mprgate.mpr.ca>, stone at mars.mpr.ca (Darren Stone) wrote:
>Hi.  Just a simple request I hope somebody can
>easily pull out of their toolbox...

Well,... I've written date routines before (in REXX & BASIC, of all
languages!), and most of them have turned out to be wrong. But I
just fixed one of them, so it should be OK now. With any luck :-)

>I'd like a function which, given a date, returns
>0..6 representing the day of the week of the date.

>I'd also like a function which, given 2 dates,
>returns the number of days between them.

>I really don't care at all about efficiency, but
>they must work absolutely reliably for +/- several
>hundred years.

They should work for any date since the invention of the modern calendar.
However, since I have no real way of telling whether May 21st 1832 was
actually a Monday, I can't guarantee it :-)

I solved both problems by assigning a number to each date (essentially the
number of days since Jan 1, 1 A.D., although of course that date is
meaningless in practice). So if you have dates d1 and d2, which give rise
to numbers n1 and n2, then there are n2-n1 days between them, and moreover
n1 mod 7 gives you the day of the week of d1. You don't need to store the
dates really, just the numbers.

First, define a couple of functions.

let leap(y) = (y%4==0) && (y%100!=0) || (y%400==0)
let days(y) = ((y-1)*1461)/4 - (3+3*((y-1)/100))/4 /*long integer arithmetic*/

which is (I hope) equivalent to int(365.25*(y-1)) + int(-.75*int((y-1)/100))
where int(x) is the greatest integer which is no more than x, so that days(y)
is the number of days before year y (the right-hand half accounts for the
leap year rule for multiples of 100). This is the really-important-function :-)

Let an array m indexed from 0 to 11 hold the number of days in each month.

To turn a date (day,month,year) into a number n, do:

if(year<100) year+=1900;
n=days(year)+day-1;
m(1)=28+leap(year);
for(x=0;x<month-1;n+=m(x++));

To turn a number back into a date (weekday,day,month,year), do:

weekday=n%7;     /* 0-6 -> Monday - Sunday */
year=(n*4)/1461 +1;
day=n-days(year);
while(d>=365+leap(year)) day-= 365=leap(year++);
m(1)=28+leap(year);
day++;
for(month=0;day>m(month);day-=m(month++));
month++;

The maximum number of times around the while loop is 2, at least within the
next few millenia :-)

Ian Collier
Ian.Collier at prg.ox.ac.uk | imc at ecs.ox.ac.uk



More information about the Comp.lang.c mailing list