home *** CD-ROM | disk | FTP | other *** search
- (*:Name: `Miscellaneous`Calendar *)
-
- (*:Title: Unifiying Calendar Computations by Considering a Calendar
- as a Mixed Radix Representation Generalized to Lists.
- *)
-
- (*:Author: Ilan Vardi *)
-
- (*:Mathematica Version: 2.0 *)
-
- (*:Package Version: 1.01 *)
-
- (*:History: V1.0, Ilan Vardi
- V1.01, minor revisions, John M. Novak, Feb. 1992
- *)
-
- (*:Keywords: Calendar, Julian, Gregorian, Islamic, Digits.
- *)
-
- (*:Requirements: none. *)
-
- (*:Warnings: A date is written as {y, m, d} where y is the year,
- m is the month, and d is the day.
-
- The computations can be done either in the Gregorian,
- Julian, or Islamic calendars. The Gregorian calendar is
- the usual calendar in use today and this calendar is
- taken to be the default if no calendar is specified.
-
- Great Britain and its colonies switched from the Julian
- calendar to the Gregorian calendar in September, 1752.
- In these countries {1752, 9, 2} was followed by
- {1752, 9, 14}. The default calendar for dates on
- or before {1752, 9, 2} is taken to be the Julian calendar.
- This requires making some adjustements to DayOfWeek,
- DaysBetween, and DaysPlus. For example,
- DaysBetween[{1752, 9, 2}, {1752, 9, 14}] will return 1.
-
- Catholic countries switched from the Julian to the Gregorian
- calendar in October 1582, so that {1582, 10, 4} was followed
- by {1582, 10, 15}. This change to the Package can be made
- quite easily.
-
- The algorithm for the Julian calendar will fail for the year
- 4 and earlier since the leap years from 40 B.C. to the
- year 4 did not follow a regular pattern (see Bickerman's book),
- and also the year zero does not exist (it is taken to be
- 1 B.C.). The first valid Julian date is therefore {4, 3, 1}.
-
- This only computes the Jewish New Year for the years 1900
- up to 2099.
-
- *)
-
- (*:Source: Ilan Vardi, Computational Recreations in Mathematica,
- Addison Wesley 1991, Chapter 3
-
- E.R. Berlekamp, J.H. Conway, and R.K. Guy, Winning Ways,
- Volume 2, Academic Press 1982, pages 795-800
-
- W.A. Schocken, The Calculated Confusion of the Calendar,
- Vantage Press, New York 1976
-
- E.J. Bickerman, The Chronology of the Ancient World,
- Revised Edition, Thames and Hudson, London 1980
- *)
-
- (*:Limitations: This package is meant to show how Mathematica can be
- used to give a unified treatment of a problem usually
- done using specialized hacks. This means that these
- functions can be speeded up somewhat. For example,
- DayOfWeek can be computed efficiently for the Gregorian
- calendar by using an algorithm of Reverend Zeller. See
- Computational Recreations in Mathematica, Problem 3.1.
-
- I have not yet implemented the Jewish calendar. This
- calendar is the most nontrivial of all, since it is the
- only one that keeps track of both the lunar and solar
- periods (see Schocken's book). A Lisp program implementing
- the Jewish calendar is given by E.M. Reingold and N. Dershowitz,
- Calendrical Calculations, Technical Report
- UIUCCDCS-R-89-1541 (1989), Dept. of Computer Science,
- Univ. of Ill. at Urbana-Champaign.
- *)
-
- (*:Discussion: This package was written in order to give a unified
- treatement of the basic calendar operations. This is
- done by considering the calendar as a mixed radix
- positional number system where the radices are lists.
- This requires a further generalization as the radix must
- actually be a tree. This is necessary since, for example,
- the numbers of days in a month depend on what year it is.
- The calendars are quite regular, so they are most
- compactly represented as trees of functions. See
- Chapter 3 of Computational Recreations in Mathematica
- for details.
-
- The Julian calendar is the simplest calendar consisting
- of the usual western calendar with leap years every
- four years. This calendar gives a year that is slightly
- too long. It was replaced with the Gregorian calendar
- in Catholic countries in 1582 and by Britain and its
- colonies in 1752. It is still used to compute Greek
- Orthodox holidays, such as Easter.
-
- The Gregorian calendar is the calendar presently in
- use in the western world. It differs from the Julian
- calendar by eliminating leap years for centuries not
- divisible by 400. In other words, 1900 was not a leap
- year, but the year 2000 will be a leap year.
-
- The Islamic calendar is a purely lunar calendar, and a year
- has 354 or 355 days. The months do not correspond to the
- solar year, and migrate over the solar year following a
- 30 year cycle. The names of the months are
-
- Muharram, Safar, Rabia I, Rabia II, Jumada I, Jumada II,
- Rajab, Sha'ban, Ramadan, Shawwal, Dhu al-Qada, Dhu al-Hijah
-
- The functions computing Easter and the Jewish New Year
- are taken directly from Winning Ways.
- *)
-
- BeginPackage["Miscellaneous`Calendar`"]
-
- DayOfWeek::usage = "DayOfWeek[{y, m, d}, cal] gives the day of the
- week for year y, month m, and day d in calendar cal. The default
- calendar is the usual American calendar."
-
- DaysBetween::usage = "DaysBetween[{y1,m1, d1}, {y2,m2,d2}, cal] gives
- the number of days between the dates {y1, m1, d1} and {y2, m2, d2}
- in calendar cal. The default calendar is the usual American calendar."
-
- DaysPlus::usage = "DaysPlus[{y, m, d}, n, cal] gives the date n days
- after {y, m, d} in calendar cal. The default calendar is the usual
- American calendar."
-
- CalendarChange::usage = "CalendarChange[date, calendar1, calendar2]
- converts a date in calendar1 to a date in calendar2."
-
- Calendar::usage = "Calendar indicates which calendar system is being
- used. Either the Gregorian, Julian, or Islamic calendars."
-
- Gregorian::usage = "An option to DayOfWeek, DaysBetween, and DaysPlus,
- and argument to CalendarChange. It indicates that the Gregorian
- calendar is used. It is assumed that the date must be {1752, 9, 14} or
- later."
-
- Julian::usage = "An option to DayOfWeek, DaysBetween, and DaysPlus,
- and argument to CalendarChange. It indicates that the Julian calendar
- is used. This calendar is only valid starting {4, 3, 1}."
-
- Islamic::usage = "An option to DayOfWeek, DaysBetween, and DaysPlus,
- and argument to CalendarChange. It indicates that the Islamic calendar
- is used. This calendar began on {622, 7, 16} Julian, in other words,
- this is {1, 1, 1} in the Islamic calendar (the Hejira)."
-
- EasterSunday::usage = "EasterSunday[year] computes the date of Easter
- Sunday in the Gregorian calendar according to the Gregorian
- calculation."
-
- EasterSundayGreekOrthodox::usage = "EasterSunday[year] computes the
- date of Easter Sunday according to the Greek Orthodox church. The
- result is given as a Gregorian date."
-
- JewishNewYear::usage = "JewishNewYear[y] gives the date of the Jewish
- New Year occurring in Christian year y, 1900 <= y < 2100. Add 3761 to
- Christian year y to get the corresponding new Jewish Year. "
-
- Sunday::usage = "Day of Week."
-
- Monday::usage = "Day of Week."
-
- Tuesday::usage = "Day of Week."
-
- Wednesday::usage = "Day of Week."
-
- Thursday::usage = "Day of Week."
-
- Friday::usage = "Day of Week."
-
- Saturday::usage = "Day of Week."
-
-
- Begin["`Private`"]
-
-
- Options[DayOfWeek]:= {Calendar -> Gregorian}
-
- Options[DaysBetween]:= {Calendar -> Gregorian}
-
- Options[DayPlus]:= {Calendar -> Gregorian}
-
- DayOfWeekNumber[date_List, opts___]:=
- Block[{calendar = Calendar /. {opts}},
- If[calendar === Calendar,
- calendar = If[OrderedQ[{date, {1752, 9, 2}}],
- Julian, Gregorian]];
- Mod[DateToNumber[date, calendar] + DayOfWeekInit[calendar], 7]
- ]
-
- DayOfWeek[date_List, opts___]:=
- {
- Sunday, Monday, Tuesday, Wednesday, Thursday, Friday, Saturday
- }[[
- 1 + DayOfWeekNumber[date, opts]
- ]]
-
-
- DayOfWeekInit[Gregorian] = 0
-
- DayOfWeekInit[calendar_]:= DayOfWeekInit[calendar] =
- Mod[3 -
- DateToNumber[
- CalendarChange[{1990, 10, 31}, Gregorian, calendar],
- calendar],
- 7]
-
-
- DaysBetween[date1_List, date2_List, opts___]:= 0 /; date1 == date2
-
- DaysBetween[date1_List, date2_List, opts___]:=
- DaysBetween[date2, date1, opts] /; OrderedQ[{date2, date1}]
-
- DaysBetween[date1_List, date2_List, opts___]:=
- Block[{calendar = Calendar /. {opts}},
- If[calendar === Calendar,
- If[OrderedQ[{date1, {1752, 9, 2}}] &&
- OrderedQ[{1752, 9, 14}, date2],
- 1 + DaysBetween[date1, {1752, 9, 2}, Calendar -> Julian] +
- DaysBetween[{1752, 9, 14}, date2, Calendar -> Gregorian],
- DaysBetween[date1, date2, Calendar ->
- If[OrderedQ[{date1, {1752, 9, 2}}],
- Julian, Gregorian]]
- ],
- DateToNumber[date2, calendar] - DateToNumber[date1, calendar]
- ]
- ]
-
- DaysPlus[date_List, n_Integer, opts___]:=
- Block[{calendar = Calendar /. {opts}, d},
- If[calendar === Calendar,
- If[OrderedQ[{date, {1752, 9, 2}}],
- d = DaysPlus[date, n, Calendar -> Julian];
- If[OrderedQ[{{1752, 9, 3}, d}],
- CalendarChange[d, Julian, Gregorian],
- d],
- d = DaysPlus[date, n, Calendar -> Gregorian];
- If[OrderedQ[{d, {1752, 9, 13}}],
- CalendarChange[d, Gregorian, Julian],
- d]
- ],
- NumberToDate[DateToNumber[date, calendar] + n, calendar]
- ]
- ]
-
-
- CalendarChange[date_, calendar1_, calendar2_]:=
- NumberToDate[DateToNumber[date, calendar1] +
- CalendarChangeInit[calendar1, calendar2], calendar2]
-
-
- EasterSunday[y_Integer]:=
- Block[{paschal, golden, c, h, t},
- golden= Mod[y,19] +1;
- h = Quotient[y,100];
- c = - h + Quotient[h,4] + Quotient[8(h +11),25];
- t = DaysPlus[{y, 4, 19}, - Mod[11 golden +c, 30],
- Calendar -> Gregorian];
- paschal = If[(t[[3]] == 19) || (t[[3]] == 18 && golden >11),
- t - {0,0,1} , t];
- DaysPlus[paschal,
- 7 - DayOfWeekNumber[paschal, Calendar -> Gregorian]]
- ]
-
- EasterSundayGreekOrthodox[y_Integer]:=
- Block[{paschal, golden, c, h, t},
- golden= Mod[y,19] +1;
- h = Quotient[y,100];
- c = 3;
- t = DaysPlus[{y, 4, 19}, - Mod[11 golden +c, 30],
- Calendar -> Gregorian];
- paschal = If[(t[[3]] == 19) || (t[[3]] == 18 && golden >11),
- t - {0,0,1} , t];
- CalendarChange[DaysPlus[paschal,
- 7 - DayOfWeekNumber[paschal, Calendar -> Julian]],
- Julian, Gregorian]
- ]
-
- JewishNewYear[y_] :=
- Block[{t,g,n,f,d},
- g= Mod[12 (Mod[y,19] +1) , 19];
- t= Quotient[y,100] -Quotient[y,400] -2 +
- 765433/ 492480 g + Mod[y,4] / 4 -
- (313 y + 89091)/98496 ;
- n = Floor[t];
- f = t - n;
- d = Switch[Mod[ DateToNumber[y,9,n] [[1]],7],
- 0, n+1,
- 1, If[ f >= 23269/25920 && g >11 , n+1,n],
- 2, If[ f>= 1367/2160 && g > 6, n+2,n],
- 3, n+1,
- 4, n,
- 5, n+1,
- 6,n];
- If[d < 31,{y, 9, d},{y, 10, d - 30}]
- ]
-
-
- (* Generalization of Digits to mixed list radices *)
-
- MyDigits[n_, list_List, path_]:=
- Block[{md = MyDigitsInit[n, list, path]},
- If[Last[md] != 0,
- md,
- MapAt[# + 1&,
- MyDigitsInit[n-1, list, path],
- {-1}]
- ]]
-
- MyDigitsInit[n_, {}, _]:= {n}
-
- MyDigitsInit[n_, list_List, path_]:=
- Block[{r = MyQuotient[n, First[list][path]]},
- Prepend[MyDigits[MyMod[n, First[list] [path]],
- Rest[list], Append[path, r]],
- MyQuotient[n, First[list] [path]]]]
-
-
-
- DigitsToNumber[date_, list_, path_]:=
- 1 + date[[1]] list[[1]][path+1][[1]] + Last[date] +
- (Plus @@
- (Fold[Plus, 0, Take[list[[#]][path+1], date[[#]]]]& /@
- Range[2, Length[list]]))
-
- MyDigits[n_, b_List]:= {n} /; b == {} || 0 < n < Last[b]
-
- MyDigits[n_, b_List]:=
- Append[MyDigits[Quotient[n, Last[b]], Drop[b, -1]],
- Mod[n, Last[b]]]
-
- DigitsToNumber[{n_}, b_]:= n
-
- DigitsToNumber[digits_List, b_]:=
- DigitsToNumber[Drop[digits, -1],
- Drop[b, -1]] Last[b] + Last[digits]
-
- (* Quotient and Mod generalized to lists *)
-
- MyQuotient[n_Integer, list_List]:=
- Quotient[n, First[list]] + 1 /; Length[list] == 1
-
- MyQuotient[n_Integer, list_List]:=
- Block[{s = First[list], q = 1},
- While[n > s, q++; s += list[[q]]]; q] /; Length[list] > 1
-
- MyMod[n_Integer, list_List]:=
- Mod[n, First[list]] /; Length[list] == 1
-
- MyMod[n_Integer, list_List]:=
- n - Fold[Plus, 0, Take[list, MyQuotient[n, list]-1]] /; Length[list] > 1
-
-
-
- (* Julian calendar *)
-
- JulianCalendar = {JulianFourYears, JulianYears, JulianMonths}
-
- JulianFourYears[_]:= {1461}
-
- JulianYears[_]:= {365, 365, 365, 366}
-
- JulianMonths[path_List]:=
- {31, 28 + Quotient[path[[2]], 4],
- 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}
-
-
- NumberToDate[n_, Julian]:=
- Prepend[Drop[#, 2], DigitsToNumber[Take[# - 1, 2], {4}] + 1]& @
- MyDigits[n, JulianCalendar, {}]
-
- DateToNumber[date_, Julian]:=
- Block[{d = Join[MyDigits[First[date]-1, {4}] ,
- Rest[date]-1]},
- d = Join[Table[0, {4 - Length[d]}], d];
- DigitsToNumber[d, JulianCalendar, d]]
-
-
- (* Gregorian calendar *)
-
- GregorianCalendar =
- {GregorianFourCenturies, GregorianCentury,
- GregorianFourYears, GregorianYears, GregorianMonths}
-
- GregorianFourCenturies[_]:= {146097}
-
- GregorianCentury[_]:= {36524, 36524, 36524, 36525}
-
- GregorianFourYears[path_]:=
- Append[Table[1461, {24}], 1460 + Quotient[path[[2]], 4]]
-
- GregorianYears[path_]:=
- {365, 365, 365, 366 -
- (1-Quotient[path[[2]], 4]) Quotient[path[[3]], 25]}
-
- GregorianMonths[path_]:=
- {31, 28 + Quotient[path[[4]], 4] * (1 -
- (1 - Quotient[path[[2]], 4]) Quotient[path[[3]], 25]),
- 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}
-
- NumberToDate[n_, Gregorian]:=
- Prepend[Drop[#, 4], DigitsToNumber[Take[#-1, 4], {4,25,4}] + 1]& @
- MyDigits[n, GregorianCalendar, {}]
-
- DateToNumber[date_, Gregorian]:=
- Block[{d = Join[MyDigits[First[date]-1, {4, 25, 4}] , Rest[date]-1]},
- d = Join[Table[0, {6 - Length[d]}], d];
- DigitsToNumber[d, GregorianCalendar, d]]
-
-
- CalendarChangeInit[Gregorian, Julian] = 2
-
- CalendarChangeInit[Julian, Gregorian] = -2
-
-
- (* Islamic calendar *)
-
-
- IslamicCalendar = {IslamicThirtyYears,
- IslamicYears,
- IslamicMonths}
-
- IslamicThirtyYears[_]:= {30 354 + 11}
-
- IslamicYears[_]:= 354 +
- {0,1,0,0,1,0,1,0,0,1,0,0,1,0,0,
- 1,0,1,0,0,1,0,0,1,0,1,0,0,1,0}
-
- IslamicMonths[path_]:=
- {30, 29, 30, 29, 30, 29, 30, 29, 30, 29, 30, 29 +
- {0,1,0,0,1,0,1,0,0,1,0,0,1,0,0,
- 1,0,1,0,0,1,0,0,1,0,1,0,0,1,0}[[path[[2]]]]}
-
-
- NumberToDate[n_, Islamic]:=
- Prepend[Drop[#, 2], DigitsToNumber[Take[#-1, 2], {30}] + 1]& @
- MyDigits[n, IslamicCalendar, {}]
-
- DateToNumber[date_, Islamic]:=
- Block[{d = Join[MyDigits[First[date]-1, {30}],
- Rest[date]-1]},
- d = Join[Table[0, {4 - Length[d]}], d];
- DigitsToNumber[d, IslamicCalendar, d]]
-
- CalendarChangeInit[Islamic, Julian] =
- DateToNumber[{622, 7, 15}, Julian]
-
- CalendarChangeInit[Julian, Islamic] =
- -CalendarChangeInit[Islamic, Julian]
-
- CalendarChangeInit[Gregorian, Islamic] =
- -DateToNumber[CalendarChange[{622, 7, 15}, Julian, Gregorian],
- Gregorian]
-
- CalendarChangeInit[Islamic, Gregorian] =
- -CalendarChangeInit[Gregorian, Islamic]
-
-
- End[] (* Miscellaneous`Calendar`Private` *)
-
- Protect[DayOfWeek, DaysBetween, DaysPlus, CalendarChange, Calendar,
- Julian, Gregorian, Islamic, EasterSunday, JewishNewYear,
- EasterSundayGreekOrthodox]
-
-
-
- EndPackage[] (* Miscellaneous`Calendar` *)
-
-
-
-