From: | Mark J. Reed <markjreed@...> |
---|---|

Date: | Sunday, April 25, 2004, 18:38 |

If you want to manipulate dates programmatically that aren't in the usual (Gregorian) calendar, you have to roll your own. I mentioned _Calendrical_Calculations_, which is a good discussion of the topic. The source code for the library from the book (called "Calendrica") is freely available - it's written in Common Lisp, but there's also a Java implementation of the same idea available from calendarists.com, and I have ported the arithmetical calendar subset to both Perl and Ruby. The basic idea is to avoid whatever Date API the language provides, because it is invariably too limited for use with a different calendar system. Use an absolute day count as an intermediary - either the Julian Day or a different one; the Calendrica library uses a count based on RD = Rata Die ("fixed day") 1 = January 1, 1 AD. To implement a calendar system, you need only provide two functions - one to convert from the absolute day count, and one to convert to it. For instance: suppose you want to know the date of Passover next year. Well, the date of Passover is fixed in the Hebrew calendar - it's the 15th day of the month Nisan, and next year is year 5765 in the Jewish era (AM = Anno Mundi "Year of the world"), so the trivial answer is that the date of the next Passover is Nisan 15, 5765 AM. But what you probably really want to know is what date in the Gregorian calendar that translates to. Using the Calendrica system, you first ask "What is the RD number of Nisan 15, 5765 AM in the Hebrew calendar?" The answer is RD 732,061. You then ask "What is the Gregorian date corresponding to RD 732,061?" The answer is Monday, April 25, 2005. (That means that Passover Seders will be eaten across the land on Sunday evening, April 24, because the Jewish date begins at sunset; when the sun goes down Sunday evening, it becomes Monday in the Jewish reckoning). Actually writing the conversions can be a little tricky, and this is where the discussion in the book comes in handy, but I'll talk about the Gregorian conversions. First, you have to figure out the "epoch" of the calendar. This is the calendar's logical "first day" - not when it was actually adopted, but the date you get if you extend the system backwards in time to its logical beginning. For instance, the epoch of the Gregorian calendar is January 1st, 1 AD, even though in 1 AD the invention of the Gregorian calendar was still over 1500 years in the future and even the convention of counting years AD was over 500 years in the future. You have to find out somehow what the absolute day number of the epoch is. In this case it's easy, since the RD system is defined such that RD 1 = January 1st, 1 AD Gregorian. In other cases you have to use external references, usually a date in some other calendar whose conversion functions have been written for you. For instance, several sources will tell you that the Hebrew calendar epoch - that is, Tishri 1, 1 AM - corresponds to Monday, October 7, 3761 BC in the Julian calendar, which is September 7, 3761 BC in the Gregorian calendar, which is RD -1,373,427. Anyway. Take a given date in the Gregorian calendar - say, my birthday: May 5, 1968. What's the RD for that date, given that RD 1 = January 1, 1? The calculation goes as follows: Start with the RD of the epoch: 1 Add the number of days in complete years, ignoring leap years: 1967 * 365 = 717,955 + 717,955 = 717,956 Add the number of leap days, ignoring the Gregorian reform and assuming every fourth year is leap: floor(1967 / 4) = floor(491.75) = 491 + 491 = 718,447 Subtract the number of centennial years, since those aren't leap in the Gregorian system: floor(1967 / 100) = floor(19.67) = 19 - 19 = 718,428 Add back the number of centennial years which are divisible by 400, since those are leap after all: floor(1967 / 400) = floor(4.9175) = 4 + 4 = 718,432 That gives you the RD of January 1, 1968. Now you just have to count forward through the year: January had 31 days + 31 = 718,463 (718,463 is the RD of Feb 1) 1968 was a leap year, so February had 29 days + 29 = 718,492 March had 31 days + 31 = 718,523 April had 30 days + 30 = 718,553 That takes us to May 1; add 4 to get to the 5: + 4 = 718,557 So I was born on RD 718,557. Going the other way is more complicated; usually one direction or the other is going to be harder with any given system. The important thing when going from the RD to a calendar date is identifying the repeating cycles of days. In the Gregorian calendar, the longest cycle is 400 years, which comprise exactly 146,097 days or exactly 20,871 weeks. Within each 400-year cycle there are four 100-year cycles, of which the first three (which end in non-leap century years) are 36,524 days each, while the last (which ends in a leap century year) is 36,525 days. Each 100-year cycle consists of 25 four-year cycles, most of which are 1,461 days, but the last four-year cycle of each of the first three 100-year cycles is only 1,460 days due to the skipped leap year. Finally, each four-year cycle consists of four years - the first three being 365 days each, the last 366. So to go from the RD to the Gregorian date you have to descend through these cycles. Let's find out when I will turn 15,000 days old: that's RD 718,557 + 15,000 = 733,557. Again, the beginning of the epoch is RD 1, so that's 733,557 days into the epoch. How many 400-year cycles is that? 733,557 / 146,097 = 5 with remainder 3,072 That's five complete 400-year cycles plus 3,072 more days, which means 3,072 days into the sixth 400-year cycle. How many 100-year cycles into the next 400-year cycle is that? 3,072 / 36,524 = 0 with remainder 3,072 Less than 100 years. How many four-year cycles? 3,072 / 1,461 = 2 with remainder 150 So two complete four-year cycles - 8 years - plus 150 more days. How many years is that? 150 / 365 = 0 with remainder 150 Obviously, 150 days is less than one year. So we have determined that I become 15,000 days old 150 days into the first year of the third four-year cycle of the first century of the 6th 400-year cycle of the Gregorian epoch. What does that mean in English? Well, 5 complete 400-year cycles is 2000 years: 2000 Plus zero complete 100-year cycles is 0: 2000 Plus two complete 4-year cycles is 8 years: 2008 But we're 150 days past the end of 2008, so that's 2009. So the 150th day of 2009. What is that in usual terms? If you think of it as "January 150th", then you can just subtract the number of days in each month and advance the month name until you get something in the usual range: Subtract January: 150 - 31 = February 119th Subtract Feburary: 119 - 28 = March 91st Subtract March: 91 - 31 = April 60th Subtract April: 60 - 30 = May 30th So I will turn 15,000 days old on May 30, 2009. -Mark

Roger Mills <rfmilly@...> |