## The Design of Software (CLOSED)A public forum for discussing the design of software, from the user interface to the code architecture. Now closed. |
||

The "Design of Software" discussion group has been merged with the main
Joel on Software discussion group.
The archives will remain online indefinitely. |
It's easy enough to find an example (or even deduce it youself) of how to calculate the number of days between two dates using the Julian system (365 days per year). I was wondering though if there is a good way to do it with the slightly more accurate Gregorian system?
Part of the problem with this system is finding the leap years and I was wondering if there is an algorithm to find the number of even numbers between two other numbers? This would be the hardest part of calculating in the Gregorian system and I was unable to find anything using Google? Anyone here good at math(s), or happen to know what search terms will turn this up?
Not sure exactly what your aiming at, but a google for "IsLeapYear source" gets you this little snippet, which contains the logic necessary to get there, I think. GetDaysInYear(int year) seems pretty trivial from there.
if ( ((shYear % (short) 4) == 0) && ( ((shYear % (short) 100) != 0) || ((shYear % (short) 400) == 0) ) ) { return true; // Is Leap Year } else { return false; // Not a leap year. }
That looks like you would have to loop over each year between the two dates you are calculating the age with which is what I was trying to avoid. But if that is all there is then I guess that is the way to go. I was hoping there would be an equation that given the start and end numbers would calculate the number of even numbers between two numbers. That would make the calculation much more efficient if it existed.
I'm not sure how having "the number of even numbers between two numbers" would help. You mean an equation like this?
f(1,9) = 4 // 2, 4, 6, 8 f(1,3) = 1 // 2 f(1,4) = 1 // still just 2 f(2,4) = 0 // none f(2,5) = 1 // 4 If this is the only piece missing for you determine number of days old, you must have it broken down a lot further than I'm able! I wonder if this would work, given y >= x and the integer division truncates the fraction: f(int x, int y) = (y-x)/2 - (x%2==0 && y%2==0 ? 1 : 0) Eh, I may be way off from where you're going, but it's a fun little exercise anyway.
How about this, from the Single Unix Specification:
http://www.opengroup.org/onlinepubs/009695399/basedefs/xbd_chap04.html#tag_04_14 The number of seconds from Jan 1, 1970. Could be modified to what you want, just so long as all dates are >= 1970.
Here's what I'm thinking for calculating the age in days:
Using a Julian calendar where every year is 365 days you can take the two dates and subtract their years and then multiply by 365 as a start. Then you can figure out based on the months and days of the two dates how many more days to add to that. In a Gregorian calendar it would be similar but the year calculation would be split into two parts. numYears = year2 - year1 numLeapYears = numYears - (NumEvens(year1, year2) / 2)? numNormalYears = numYears - numLeapYears ageDays = (numLeapYears * 366) + (numNormalYears * 365) Then add in the months and days as in the Julian calendar. I haven't finished all of the details but that's the general idea I'm working towards. The tricky part is I don't know if the math in calculating numLeapYears is sound. BTW I'm using the Java Calendar class to do some of the date manipulation.
"That looks like you would have to loop over each year between the two dates you are calculating the age with which is what I was trying to avoid."
Note that there is a 400-year cycle which keeps repeating itself. If you take advantage of that, 400 doesn't seem to me a huge upper bound number of iterations. http://www.timeanddate.com/date/leapyear.html
Julian,
Your little algorithm is too simple, because you assume that January 1st is the first day of the calender. It is not. The first day of the year is March 1st. You missed the fact that the number of leap years (or actually: days), between to given years, depends on whether the days of the year are before or on/after March 1st. What you should do is normalise the Gegorian dates to a calender that starts on March 1st (like the Julian calender did originally, hence the leap day at the end of the year). For those who are interested to know: historically the leap day is not on February 29, but February 23rd (excuse my possible off-by-one error). Not that it makes any difference for your question, but it is a good Triviant-question.
I think what you want is to use the Julian Day number. This assigns a unique integer number to any date, going back to approximately 4700 BC. By calculating the Julian Day number for any two dates and subtracting them you can determine the number of days between the two.
Note that this is the Julian Day number, not a date on the Julian calendar. They are different. Doing a search for "julian day" on google will probably bring up several example algorithms.
There are quite a few ways of calculating the number of days from some base date, which can obviously be used to calculate the number of days between two dates. “Calendrical calculations” by Reingold and Dershowitz will probably tell you more than you want to know:
http://emr.cs.iit.edu/home/reingold/calendar-book/second-edition/index.html Duffet-Smith’s book “Practical astronomy with your calculator” has a method which works fine, but I tend to use something along the following lines these days, although it may be too terse for some tastes: int daystoeom[] = {0,31,59,90,120,151,181,212,243,273,304,334,365} ; void datetogdn(Date* date) { int yp_100, yp, mm; yp = date->year; mm = date->month; if (mm <= 2) yp = yp - 1; yp_100 = yp/100; date->gdn = (365L * date->year) + daystoeom[mm-1] + date->day + (yp>>2) + (yp_100>>2) - yp_100 - base_days; } In practice, you’re also pretty safe using the standard C stuff in time.h & assuming that time_t is in seconds, and that the system doesn’t know about leap seconds so you can just divide by 86400 to get days. Doesn’t work pre 1970 though.
as Tuesday, October 05, 2004
Possibly I am missing something but if you are using the java calendar utilities couldn't you just get a java.util.Date representing each date, call getTime() to get the long representation of each and then subtract? This gets you the difference in milliseconds and simple division could convert it to days. If you are really anal there is a slight chance you could be off because of a leap second.
To the guy who said the year starts on March 1st...huh? Reference please?
name withheld out of cowardice Thursday, October 07, 2004
That is me.
I wrote 'first day of the calender', not 'first day of the year'. An important difference. Taking January 1st as the start of the year is a modern convention introduced some places as late as the 18th century. I think the start of the year used to be celabrated in England as 'Mayday'. The names of the months still provide a clue as does the position of the leapday. More information via Google. If you use broadband I can email you the paperback with the details (in Dutch). Calender calculations are greatly simplied if the date is first normalised to a calender that starts on March 1st. Then there is even a regularity in the number of days in each month. The calculation becomes so simple that I can mentally calculate the day of the week of any date using these normalised dates.
The getTimeInMillis function can be used to do this but it does not work for dates before 1/1/1970.
Karel: Do you have an implementation of this? The normalization and the calculations based off the normalized date?
I think you need to look at the date and time library functions for whatever language / platform you're using.
If you really want to do it the hard way, here's a hint about finding the number of evens strictly between a and b (a, b presumed integral with a < b) You can safely round a up to the nearest even number above it (if it's not already even), and b down to the nearest number below it (likewise), and still have the same number of evens strictly between them. You can use this to reduce the 4 special cases (a and b both odd, both even, a odd b even, a even b odd) down to just one, which is then trivial. You'll still need one extra special case for when a=b as this may confuse it. Surely knowing the number of multiples of 4 between a and b would be more use though? again the technique above will help even more here as it there are more special cases it gets rid of. Just rounding up or down to a multiple of 4. Which is particularly thinking about it in binary. You do know that every (I think) 200 years the leap year actually isn't a leap year? for example the year 2000 wasn't a leap year. Modular arithmetic - dontcha just love it
Forget directly calculating the difference in days. Just calculate the Gregorian numbers for both dates and subtract these.
To give you an indication how to do this (I do not have the complete algorithm at hand): function gregorian(y, m, d: Int): Int -- -- first normalise the input -- then calculate the Julian number -- then make correction to obtain Gregorian number -- if m< 3 then m2 := m+9; y2 := y-1 else m2 := m-3; y2 := y; julian := int(y2*365.25)+int(m2*30.6+0.5)+d return julian - correctionForEvery100and400yrs + calibration If well-calibrated, the function returns 1 for the first date of the Gregorian calendre, i.e. 0001.03.01. Now do not ask me to give you the inverse of this function (-8.
Matt why do you assume I haven't, if it was easy I wouln't be asking here. I found many threads on Sun's Java forums asking how to do this, but all of them are for years and none of them are cut and dry (there were at least three implementations proposed). I wanted to see what it would take to get the age in days instead and learned that it's not simple either. I admit that I'm kind of suprised that Java doesn't have this functionality already but they do not.
Justin, what is wrong with the method I suggested?
name withheld out of cowardice Thursday, October 14, 2004 |

Powered by FogBugz