UNPKG

6.65 kBJavaScriptView Raw
1/**
2 * @copyright 2013 Sonia Keys
3 * @copyright 2016 commenthol
4 * @license MIT
5 * @module jm
6 */
7/**
8 * JM: Chapter 9, Jewish and Moslem Calendars.
9 *
10 * The Jewish calendar routines are implemented as a monolithic function,
11 * because computations of the various results build off of common
12 * intermediate results.
13 *
14 * The Moslem calendar routines break down nicely into some separate functions.
15 *
16 * Included in these are two functions that convert between Gregorian and
17 * Julian calendar days without going through Julian day (JD). As such,
18 * I suppose, these or similar routines are not in chapter 7, Julian Day.
19 * Package base might also be a suitable place for these, but I'm not sure
20 * they are used anywhere else in the book. Anyway, they have the quirk
21 * that they are not direct inverses: JulianToGregorian returns the day number
22 * of the day of the Gregorian year, but GregorianToJulian wants the Gregorian
23 * month and day of month as input.
24 */
25
26import base from './base';
27import julian from './julian';
28
29var int = Math.trunc;
30
31/**
32 * JewishCalendar returns interesting dates and facts about a given year.
33 *
34 * Input is a Julian or Gregorian year.
35 *
36 * Outputs:
37 * A: (int) Year number in the Jewish Calendar
38 * mP: (int) Month number of Pesach.
39 * dP: (int) Day number of Pesach.
40 * mNY: (int) Month number of the Jewish new year.
41 * dNY: (int) Day number of the Jewish new year.
42 * months: (int) Number of months in this year.
43 * days: (int) Number of days in this year.
44 */
45export function JewishCalendar(y) {
46 var A = y + 3760;
47 var D = bigD(y);
48 var mP = 3;
49 var dP = D;
50 if (dP > 31) {
51 mP++;
52 dP -= 31;
53 }
54 // A simplification of Meeus's rule to add 163 days. Months of Pesach
55 // are either March or April with D based off of March. Months of New
56 // year are either September or August so D+163-(days from March to
57 // September == 184) = D-21 must be based off of September.
58 var mNY = 9;
59 var dNY = D - 21;
60 if (dNY > 30) {
61 mNY++;
62 dNY -= 30;
63 }
64 var months = 12;
65 switch (A % 19) {
66 case 0:
67 case 3:
68 case 6:
69 case 8:
70 case 11:
71 case 14:
72 case 17:
73 months++;
74 break;
75 }
76 // Similarly, A simplification of Meeus's rule to take the difference
77 // in calendar days from NY of one year to NY of the next. NY is based
78 // on D, so difference in D is difference in day numbers of year. Result
79 // is sum of this number and the number of days in the Western calandar
80 // year.
81 var y1 = y + 1;
82 var lf = julian.LeapYearGregorian;
83 if (y1 < 1583) {
84 lf = julian.LeapYearJulian;
85 }
86 var days = 365;
87 if (lf(y1)) {
88 days++;
89 }
90 days += bigD(y1) - D;
91 return [A, mP, dP, mNY, dNY, months, days];
92}
93
94var bigD = function bigD(y) {
95 // (y int) int
96 var C = base.floorDiv(y, 100);
97 // const S int
98 var S = 0;
99 if (y >= 1583) {
100 S = int(base.floorDiv(3 * C - 5, 4));
101 }
102 var a = (12 * y + 12) % 19;
103 var b = y % 4;
104 var Q = -1.904412361576 + 1.554241796621 * a + 0.25 * b - 0.003177794022 * y + S;
105 var fq = Math.floor(Q);
106 var iq = int(fq);
107 var j = (iq + 3 * y + 5 * b + 2 - S) % 7;
108 var r = Q - fq;
109 // const D int
110 var D = void 0;
111 if (j === 2 || j === 4 || j === 6) {
112 D = iq + 23;
113 } else if (j === 1 && a > 6 && r >= 0.63287037) {
114 D = iq + 24;
115 } else if (j === 0 && a > 11 && r >= 0.897723765) {
116 D = iq + 23;
117 } else {
118 D = iq + 22;
119 }
120 return int(D);
121};
122
123/**
124 * MoslemToJD converts a Moslem calendar date to a Julian Day.
125 * @param {Number} y - year in moslem calendar
126 * @param {Number} m - month
127 * @param {Number} d - day
128 * @returns {Number} jd - Julian day
129 */
130export function MoslemToJD(y, m, d) {
131 // (y, m, d int) (jY, jDN int)
132 var N = d + base.floorDiv(295001 * (m - 1) + 9900, 10000);
133 var Q = base.floorDiv(y, 30);
134 var R = y % 30;
135 var A = base.floorDiv(11 * R + 3, 30);
136 var W = 404 * Q + 354 * R + 208 + A;
137 var Q1 = base.floorDiv(W, 1461);
138 var Q2 = W % 1461;
139 var G = 621 + 28 * Q + 4 * Q1;
140 var K = base.floorDiv(Q2 * 10000, 3652422);
141 var E = base.floorDiv(3652422 * K, 10000);
142 var J = Q2 - E + N - 1;
143 var X = G + K;
144 if (J > 366 && X % 4 === 0) {
145 J -= 366;
146 X++;
147 } else if (J > 365 && X % 4 > 0) {
148 J -= 365;
149 X++;
150 }
151 var jd = base.floorDiv(36525 * (X - 1), 100) + 1721423 + J;
152 return jd;
153}
154
155/**
156 * MoslemLeapYear returns true if year y of the Moslem calendar is a leap year.
157 * @param {Number} year
158 * @returns {Boolean} true if leap year
159 */
160export function MoslemLeapYear(year) {
161 // (y int) bool
162 var R = year % 30;
163 return (11 * R + 3) % 30 > 18;
164}
165
166/**
167 * JulianToMoslem takes a year, month, and day of the Julian calendar and returns the equivalent year, month, and day of the Moslem calendar.
168 *
169 * @param {Number} y - julian year
170 * @param {Number} m - julian month
171 * @param {Number} d - julian day
172 * @returns {Array} [my, mm, md]
173 */
174export function JulianToMoslem(y, m, d) {
175 // (y, m, d int) (my, mm, md int)
176 var W = 2;
177 if (y % 4 === 0) {
178 W = 1;
179 }
180 var N = base.floorDiv(275 * m, 9) - W * base.floorDiv(m + 9, 12) + d - 30;
181 var A = int(y - 623);
182 var B = base.floorDiv(A, 4);
183 var C2 = function (A) {
184 var C = A % 4;
185 var C1 = 365.25001 * C;
186 var C2 = Math.floor(C1);
187 if (C1 - C2 > 0.5) {
188 return int(C2) + 1;
189 }
190 return int(C2);
191 }(A);
192 var Dp = 1461 * B + 170 + C2;
193 var Q = base.floorDiv(Dp, 10631);
194 var R = Dp % 10631;
195 var J = base.floorDiv(R, 354);
196 var K = R % 354;
197 var O = base.floorDiv(11 * J + 14, 30);
198 var my = 30 * Q + J + 1;
199 var JJ = K - O + N - 1;
200 var days = 354;
201 if (MoslemLeapYear(y)) {
202 days++;
203 }
204 if (JJ > days) {
205 JJ -= days;
206 my++;
207 }
208 var mm = void 0;
209 var md = void 0;
210 if (JJ === 355) {
211 mm = 12;
212 md = 30;
213 } else {
214 var S = base.floorDiv((JJ - 1) * 10, 295);
215 mm = 1 + S;
216 md = base.floorDiv(10 * JJ - 295 * S, 10);
217 }
218 return { year: my, month: mm, day: md };
219}
220
221/**
222 * An MMonth specifies a month of the Moslum Calendar (Muharram = 1, ...).
223 *
224 * Upgraded to Unicode from the spellings given by Meeus.
225 * Source: http://en.wikipedia.org/wiki/Islamic_calendar.
226 */
227var mmonths = ['', 'Muḥarram', 'Ṣafar', 'Rabīʿ I', 'Rabīʿ II', 'Jumādā I', 'Jumādā II', 'Rajab', 'Shaʿbān', 'Ramaḍān', 'Shawwāl', 'Dhū al-Qaʿda', 'Dhū al-Ḥijja'];
228
229/**
230 * String returns the Romanization of the month ("Muḥarram", "Ṣafar", ...).
231 */
232export function moslemMonth(m) {
233 return mmonths[m];
234}
235
236export default {
237 JewishCalendar: JewishCalendar,
238 MoslemToJD: MoslemToJD,
239 MoslemLeapYear: MoslemLeapYear,
240 JulianToMoslem: JulianToMoslem,
241 moslemMonth: moslemMonth
242};
\No newline at end of file