|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
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
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.