leeke@cascade.STANFORD.EDU (Steven D. Leeke) (09/02/86)
Could someone please give me some pointers to functions for creating calendars? e.g. for any day from 1900 to as far in the future as possible. Actual code would be GREATLY appreciated - C preferred. Thanks, -- Steven D. Leeke, Center for Integrated Systems, Stanford University {ucbvax,decvax}!decwrl!glacier!leeke, leeke@cascade.stanford.edu Disclaimer: I disclaim any knowledge of the above message and its contents.
dml@loral.UUCP (Dave Lewis) (09/10/86)
In article <206@cascade.STANFORD.EDU> leeke@cascade.UUCP (Steven D. Leeke) writes: >Could someone please give me some pointers to functions for creating >calendars? e.g. for any day from 1900 to as far in the future as possible. >Actual code would be GREATLY appreciated - C preferred. > Here's a collection of calendar functions; writing a main() driver function should be trivial. These were originally assignments for a Pascal class (what the hey, an easy 4 credits with an A+) and I rewrote them in C for the hell of it. Good for contrasting Pascal and C - things I had to work around in Pascal came out as straight code in C. /* Determines whether a given year is a leap year */ leapyear (year) int year; { if (year % 4 == 0 && (year % 100 != 0 || year % 400 == 0)) return (1); else return (0); } /* Returns #days in month given month and year, taking * leap years into account for February. */ daysinmonth (month, year) int month, year; { if (month == 2) /* Is it February? */ if (leapyear (year)) /* If so, is it a leap year? */ return (29); /* 29 days in Feb in leap year */ else return (28); /* 28 days if not */ else{ if (month > 7) /* Is it August -> December? */ month++; /* Invert even/odd state if so */ if (month & 1) /* Odd months have 31 days */ return (31); else return (30); /* Even months have 30 days */ } } /* Determines whether a given date is valid */ validdate (month, day, year) int month, day, year; { if (month < 1 || month > 12 || day < 1 || day > daysinmonth (month, day, year) || year < 1583 || year > 9999) return (0); else return (1); } /* Given a valid date (month, day, and year) Zeller will * return an integer representing the day of week that * date will fall on. 0 = Sunday, 6 = Saturday. */ zeller (month, day, year) int month, day, year; { int century; month -= 2; /* Years start on March 1 so adjust standard date */ if (month < 1) { month += 12; year--; } century = year / 100; year = year % 100; return (((2.6 * month - 0.1) + day + year + year / 4 + century / 4 - century * 2) % 7); } These functions will work for any date between March 1, 1583 and December 31, 9999. Have fun! ------------------------------- Dave Lewis Loral Instrumentation San Diego hp-sdd --\ ihnp4 --\ sdcrdcf --\ bang --\ kontron -\ csndvax ---\ calmasd -->-->!crash --\ celerity --->------->!sdcsvax!sdcc3 --->--->!loral!dml (uucp) dcdwest ---/ gould9 --/ When the government runs it, it's called a Lottery. When somebody else runs it, it's called a Numbers Racket. -------------------------------
greg@ncr-sd.UUCP (Greg Noel) (09/12/86)
In article <1229@loral.UUCP> dml@loral.UUCP (Dave Lewis) writes: >/* Given a valid date (month, day, and year) Zeller will > * return an integer representing the day of week that > * date will fall on. 0 = Sunday, 6 = Saturday. > */ > >zeller (month, day, year) >int month, day, year; >{ > [ various code deleted..... ] > return (((2.6 * month - 0.1) + day + year + year / 4 > + century / 4 - century * 2) % 7); >} Note that the expression above is in floating point. This may produce bogus results if the divisions (which are supposed to be truncated) end up being done in floating point (they \shouldn't/ be, but....). Certainly, converting all those ints to floats to be added and converted back to an int will take a lot of time. Also, it is possible that the major expression (prior to the modulo operation) can be negative, so it is best to add in a bias value that is zero mod seven to be sure that the calculated modulus is also positive. Thus, a logically equivalent version, done with all integer arithmetic is: return ((26 * month - 1)/10 + day + year + year/4 + century/4 - century*2 + 777) % 7; BTW, the reason it's called a congruence is that with this formulation it's easy to demonstrate that the the day-of-the-year pattern repeats every three hundred years. -- -- Greg Noel, NCR Rancho Bernardo Greg@ncr-sd.UUCP or Greg@nosc.ARPA
devine@vianet.UUCP (Bob Devine) (09/12/86)
Dave Lewis writes: > leapyear (year) > int year; > { > if (year % 4 == 0 && (year % 100 != 0 || year % 400 == 0)) > return (1); > else > return (0); > } While this works, it is overkill. Unless you believe that your code will make it to the year 2100, a simple test for divisibility by 4 is sufficient. If you really want an algorithm for all years, you then need to also test for years divisible by 4000... int leapyear(year) int year; { /* works for the years 1901-2099 */ return(year%4); } Or if you want a simple cpp macro: #define isleapyear(year) ((year)%4) Bob Devine
pdg@ihdev.UUCP (P. D. Guthrie) (09/15/86)
>Or if you want a simple cpp macro: > >#define isleapyear(year) ((year)%4) > >Bob Devine Or if you want a complex cpp macro to return the number of days in a year...... #define num_day(x) ((x)%4 ? (x)%100 ? (x)%400 ? 366 : 355 : 356 : 355)
franka@mmintl.UUCP (09/15/86)
In article <1229@loral.UUCP> dml@loral.UUCP (Dave Lewis) writes: >daysinmonth (month, year) >int month, year; >{ > if (month == 2) /* Is it February? */ > if (leapyear (year)) /* If so, is it a leap year? */ > return (29); /* 29 days in Feb in leap year */ > else > return (28); /* 28 days if not */ > else{ > if (month > 7) /* Is it August -> December? */ > month++; /* Invert even/odd state if so */ > if (month & 1) /* Odd months have 31 days */ > return (31); > else > return (30); /* Even months have 30 days */ > } >} This is ok, but it is easier to use a table: daysinmonth(month, year) int month, year) { int monthlengths[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; if (month == 2 && leapyear(year)) { return (29); } else { return monthlengths[month - 1]; } } (Comments are left as an exercise for the reader.) Frank Adams ihnp4!philabs!pwa-b!mmintl!franka Multimate International 52 Oakland Ave North E. Hartford, CT 06108
levy@ttrdc.UUCP (Daniel R. Levy) (09/17/86)
In article <34@vianet.UUCP>, devine@vianet.UUCP (Bob Devine) writes: >Dave Lewis writes: >> leapyear (year) >> int year; >> { >> if (year % 4 == 0 && (year % 100 != 0 || year % 400 == 0)) >> return (1); >> else >> return (0); >> } > > While this works, it is overkill. Unless you believe that your >code will make it to the year 2100, a simple test for divisibility >by 4 is sufficient. If you really want an algorithm for all years, >you then need to also test for years divisible by 4000... > ... > >#define isleapyear(year) ((year)%4) > >Bob Devine Butbutbut... whattabout code which deals with things that are of concern over more than a few years' scope, such as positions of the heavenly bodies? The code needn't last till the year 2100 to want to deal with things that are going to happen then (only the printouts :-) ). And what about birth dates of very old people? -- ------------------------------- Disclaimer: The views contained herein are | dan levy | yvel nad | my own and are not at all those of my em- | an engihacker @ | ployer or the administrator of any computer | at&t computer systems division | upon which I may hack. | skokie, illinois | -------------------------------- Path: ..!{akgua,homxb,ihnp4,ltuxa,mvuxa, go for it! allegra,ulysses,vax135}!ttrdc!levy
levy@ttrdc.UUCP (Daniel R. Levy) (09/17/86)
In article <886@ihdev.UUCP>, pdg@ihdev.UUCP (P. D. Guthrie) writes: >>Or if you want a simple cpp macro: >> >>#define isleapyear(year) ((year)%4) >> >>Bob Devine > >Or if you want a complex cpp macro >to return the number of days in a year...... > >#define num_day(x) ((x)%4 ? (x)%100 ? (x)%400 ? 366 : 355 : 356 : 355) ^^^ ^^^ ^^^ Huh? -- ------------------------------- Disclaimer: The views contained herein are | dan levy | yvel nad | my own and are not at all those of my em- | an engihacker @ | ployer or the administrator of any computer | at&t computer systems division | upon which I may hack. | skokie, illinois | -------------------------------- Path: ..!{akgua,homxb,ihnp4,ltuxa,mvuxa, go for it! allegra,ulysses,vax135}!ttrdc!levy
ron@brl-sem.ARPA (Ron Natalie <ron>) (09/17/86)
In article <886@ihdev.UUCP>, pdg@ihdev.UUCP (P. D. Guthrie) writes: > to return the number of days in a year...... > > #define num_day(x) ((x)%4 ? (x)%100 ? (x)%400 ? 366 : 355 : 356 : 355) Say what? My calendar never has 355 or 356 days a year.
rcd@nbires.UUCP (Dick Dunn) (09/18/86)
In article <1193@ttrdc.UUCP>, levy@ttrdc.UUCP (Daniel R. Levy) writes: > In article <34@vianet.UUCP>, devine@vianet.UUCP (Bob Devine) writes: [example of leap-year test covering 4, 100, 400] > > While this works, it is overkill. Unless you believe that your > >code will make it to the year 2100, a simple test for divisibility > >by 4 is sufficient... > Butbutbut... whattabout code which deals with things that are of concern > over more than a few years' scope, such as positions of the heavenly bodies? Butbutbut...the argument to the procedure was an int year, apparently CE. So if you're worrying about getting it absolutely correct, don't forget the Gregorian correction! Leap years are child's play next to that one. No, really, look: FIRST, decide the domain for the function. THEN design the function to work well in that domain. IF someone tells you that the function doesn't work for his problem (outside the domain of the function), tell him that hammers don't work for sawing wood, either. -- Dick Dunn {hao,ucbvax,allegra,seismo}!nbires!rcd (303)444-5710 x3086 ...Are you making this up as you go along?
karl@haddock (09/18/86)
vianet!devine (Bob Devine) writes: >Dave Lewis writes: >> if (year % 4 == 0 && (year % 100 != 0 || year % 400 == 0)) > >While this works, it is overkill. Unless you believe that your >code will make it to the year 2100, a simple test for divisibility >by 4 is sufficient. It looks like you're assuming the test is for the current year. For a general-purpose algorithm (as per the original request), Dave's code is correct. If the input is a 32-bit signed integer measuring seconds since 1970 (a common implementation of time_t), then the range is contained in (1900,2100) and the simpler algorithm works -- but this is probably not a good assumption to make; an unsigned long will reach past 2100. >If you really want an algorithm for all years, >you then need to also test for years divisible by 4000... I believe the quadrimillennium correction was never officially adopted. It's not accounted for in cal(1). Karl W. Z. Heuer (ima!haddock!karl; karl@haddock.isc.com), The Walking Lint
franka@mmintl.UUCP (Frank Adams) (09/19/86)
In article <886@ihdev.UUCP> pdg@ihdev.UUCP (55224-P. D. Guthrie) writes: >Or if you want a complex cpp macro >to return the number of days in a year...... > >#define num_day(x) ((x)%4 ? (x)%100 ? (x)%400 ? 366 : 355 : 356 : 355) If you want a macro which gives the right answer, you might instead try: #define num_day(x) ((x)%4 ? (x)%100 ? (x)%400 ? 366 : 365 : 366 : 365) Frank Adams ihnp4!philabs!pwa-b!mmintl!franka Multimate International 52 Oakland Ave North E. Hartford, CT 06108
wyatt@cfa.UUCP (Bill Wyatt) (09/19/86)
[ mass quantities of simple vs. complete leap year calculations arguments ] > > No, really, look: FIRST, decide the domain for the function. THEN design > the function to work well in that domain. IF someone tells you that the > function doesn't work for his problem (outside the domain of the function), > tell him that hammers don't work for sawing wood, either. Trouble is, in the real world, your function will be re-used outside its proper domain, and by someone who has no idea your function is even needed, much less not suited for his/her application. If the cost of doing it right is small, then do it right. The trouble with that sentence is the problem of determining the cost of not doing it right. -- Bill UUCP: {seismo|ihnp4}!harvard!talcott!cfa!wyatt Wyatt ARPA: wyatt%cfa.UUCP@harvard.HARVARD.EDU
karl@haddock (09/22/86)
cfa!wyatt (Bill Wyatt) writes: >If the cost of doing it right is small, then do it right. I knew someone who wrote a calendar program, complete with quadrimillennium correction, in assembly language on a machine that was scheduled to be junked in a month! Karl W. Z. Heuer (ima!haddock!karl or karl@haddock.isc.com), The Walking Lint
guido@mcvax.uucp (Guido van Rossum) (09/23/86)
Wouldn't Jim Cottrell love this:
#define leapyear(y) !((y)&3)
daysinmonth(m, y) {
if (m == 2) return 28 + leapyear(y);
return 30 + ((1&m) ^ (m>7));
}
Seriously, I believe that the original code, which applied the same rule
here showed a discrepancy between its seemingly clear coding style
(explaining every simple statement with a comment) and the 'trick' in
the algorithm (invert the parity of the month for months > 7).
In my eyes the solution which uses a table is the cleanest.
devine@vianet.UUCP (Bob Devine) (09/25/86)
haddock!karl (Karl W. Z. Heuer) writes: [ that my suggestion of only checking if the year is divisible by four assumes "the test is for the current year" and that "For a general purpose algorithm (as per the original request), Dave's code is correct."] Not quite right. I said just testing by use of "year % 4" is perfectly fine for most uses (this is sort of like generic products.) However, if you want to test for years outside the range of 1901-2099 it doesn't work. The UNIX value returned by the time() system call only covers the years 1970 +/- 67 years which falls nicely in the range. The time() call is unlikely to change. If your system doesn't do it this way or you need a larger range, don't use the simple test! A general purpose algorithm must also take care of years before the Gregorian calendar system was adopted by a country. Any KGB agents out there that are reading this be sure to handle the years before 1918 differently :-) So, the posted general purpose algorithm is not comprehensive in its handling of years. My initial posting simply indicated that if you are not going to comprehensive, at least be a little faster. > I believe the quadrimillennium correction was never officially adopted. > It's not accounted for in cal(1). I'll check into the official rules; 'cal' is not a definite source. The Gregorian calender is about 1/2 minute too long compared to the solar calendar which was quite close considering it was done by a 16th century astronomer. That means that there needs to be a day added in, oh, about 3,000 years. Be sure to mark the date :-) Bob Devine
henry@utzoo.UUCP (Henry Spencer) (09/25/86)
> ...If you really want an algorithm for all years, > you then need to also test for years divisible by 4000... No, there is no 4000-year correction. There's 4 and 400 and that's it at present. Some twiddling will eventually become necessary, but it won't be anything as simple and uniform as leap millenia -- the function we are trying to approximate is not linear on that time scale. -- Henry Spencer @ U of Toronto Zoology {allegra,ihnp4,decvax,pyramid}!utzoo!henry
fgd3@jc3b21.UUCP (Fabbian G. Dufoe) (09/29/86)
In article <86900058@haddock>, karl@haddock writes: > > vianet!devine (Bob Devine) writes: > >Dave Lewis writes: > >> if (year % 4 == 0 && (year % 100 != 0 || year % 400 == 0)) > > > >While this works, it is overkill. Unless you believe that your > >code will make it to the year 2100, a simple test for divisibility > >by 4 is sufficient. It is particularly galling to see a correct algorithm criticized as overkill when it is as simple and short as the above code segment. There may be a justification for writing code that only works part of the time if the fix is costly and difficult. However, it should be a general rule that an algorithm which works in all cases is preferred over one that only works in most cases. Instead of panning someone's code because he has written it more correctly, one should adopt the improved algorithm with gratitude. Fabbian Dufoe 350 Ling-A-Mor Terrace South St. Petersburg, Florida 33705 813-823-2350 UUCP: ...akgua!akguc!codas!peora!ucf-cs!usfvax2!jc3b21!fgd3