astronomia
Version:
An astronomical library
261 lines (249 loc) • 6.69 kB
JavaScript
/**
* @copyright 2013 Sonia Keys
* @copyright 2016 commenthol
* @license MIT
* @module jm
*/
/**
* JM: Chapter 9, Jewish and Moslem Calendars.
*
* The Jewish calendar routines are implemented as a monolithic function,
* because computations of the various results build off of common
* intermediate results.
*
* The Moslem calendar routines break down nicely into some separate functions.
*
* Included in these are two functions that convert between Gregorian and
* Julian calendar days without going through Julian day (JD). As such,
* I suppose, these or similar routines are not in chapter 7, Julian Day.
* Package base might also be a suitable place for these, but I'm not sure
* they are used anywhere else in the book. Anyway, they have the quirk
* that they are not direct inverses: JulianToGregorian returns the day number
* of the day of the Gregorian year, but GregorianToJulian wants the Gregorian
* month and day of month as input.
*/
import base from './base.js'
import julian from './julian.js'
const int = Math.trunc
/**
* JewishCalendar returns interesting dates and facts about a given year.
*
* Input is a Julian or Gregorian year.
*
* Outputs:
* A: (int) Year number in the Jewish Calendar
* mP: (int) Month number of Pesach.
* dP: (int) Day number of Pesach.
* mNY: (int) Month number of the Jewish new year.
* dNY: (int) Day number of the Jewish new year.
* months: (int) Number of months in this year.
* days: (int) Number of days in this year.
*/
export function JewishCalendar (y) {
const A = y + 3760
const D = bigD(y)
let mP = 3
let dP = D
if (dP > 31) {
mP++
dP -= 31
}
// A simplification of Meeus's rule to add 163 days. Months of Pesach
// are either March or April with D based off of March. Months of New
// year are either September or August so D+163-(days from March to
// September == 184) = D-21 must be based off of September.
let mNY = 9
let dNY = D - 21
if (dNY > 30) {
mNY++
dNY -= 30
}
let months = 12
switch (A % 19) {
case 0:
case 3:
case 6:
case 8:
case 11:
case 14:
case 17:
months++
break
}
// Similarly, A simplification of Meeus's rule to take the difference
// in calendar days from NY of one year to NY of the next. NY is based
// on D, so difference in D is difference in day numbers of year. Result
// is sum of this number and the number of days in the Western calandar
// year.
const y1 = y + 1
let lf = julian.LeapYearGregorian
if (y1 < 1583) {
lf = julian.LeapYearJulian
}
let days = 365
if (lf(y1)) {
days++
}
days += bigD(y1) - D
return [A, mP, dP, mNY, dNY, months, days]
}
const bigD = function (y) { // (y int) int
const C = base.floorDiv(y, 100)
// const S int
let S = 0
if (y >= 1583) {
S = int(base.floorDiv(3 * C - 5, 4))
}
const a = (12 * y + 12) % 19
const b = y % 4
const Q = -1.904412361576 + 1.554241796621 * (a) + 0.25 * (b) -
0.003177794022 * (y) + (S)
const fq = Math.floor(Q)
const iq = int(fq)
const j = (iq + 3 * y + 5 * b + 2 - S) % 7
const r = Q - fq
// const D int
let D
if (j === 2 || j === 4 || j === 6) {
D = iq + 23
} else if (j === 1 && a > 6 && r >= 0.63287037) {
D = iq + 24
} else if (j === 0 && a > 11 && r >= 0.897723765) {
D = iq + 23
} else {
D = iq + 22
}
return int(D)
}
/**
* MoslemToJD converts a Moslem calendar date to a Julian Day.
* @param {Number} y - year in moslem calendar
* @param {Number} m - month
* @param {Number} d - day
* @returns {Number} jd - Julian day
*/
export function MoslemToJD (y, m, d) { // (y, m, d int) (jY, jDN int)
const N = d + base.floorDiv(295001 * (m - 1) + 9900, 10000)
const Q = base.floorDiv(y, 30)
const R = y % 30
const A = base.floorDiv(11 * R + 3, 30)
const W = 404 * Q + 354 * R + 208 + A
const Q1 = base.floorDiv(W, 1461)
const Q2 = W % 1461
const G = 621 + 28 * Q + 4 * Q1
const K = base.floorDiv(Q2 * 10000, 3652422)
const E = base.floorDiv(3652422 * K, 10000)
let J = Q2 - E + N - 1
let X = G + K
if (J > 366 && X % 4 === 0) {
J -= 366
X++
} else if (J > 365 && X % 4 > 0) {
J -= 365
X++
}
const jd = base.floorDiv(36525 * (X - 1), 100) + 1721423 + J
return jd
}
/**
* MoslemLeapYear returns true if year y of the Moslem calendar is a leap year.
* @param {Number} year
* @returns {Boolean} true if leap year
*/
export function MoslemLeapYear (year) { // (y int) bool
const R = year % 30
return (11 * R + 3) % 30 > 18
}
/**
* @typedef {object} DateObj
* @property {number} year
* @property {number} month
* @property {number} day
*/
/**
* JulianToMoslem takes a year, month, and day of the Julian calendar and returns the equivalent year, month, and day of the Moslem calendar.
*
* @param {Number} y - julian year
* @param {Number} m - julian month
* @param {Number} d - julian day
* @returns {DateObj}
*/
export function JulianToMoslem (y, m, d) { // (y, m, d int) (my, mm, md int)
let W = 2
if (y % 4 === 0) {
W = 1
}
const N = base.floorDiv(275 * m, 9) - W * base.floorDiv(m + 9, 12) + d - 30
const A = int(y - 623)
const B = base.floorDiv(A, 4)
const C2 = (function (A) {
const C = A % 4
const C1 = 365.25001 * (C)
const C2 = Math.floor(C1)
if (C1 - C2 > 0.5) {
return int(C2) + 1
}
return int(C2)
})(A)
const Dp = 1461 * B + 170 + C2
const Q = base.floorDiv(Dp, 10631)
const R = Dp % 10631
const J = base.floorDiv(R, 354)
const K = R % 354
const O = base.floorDiv(11 * J + 14, 30)
let my = 30 * Q + J + 1
let JJ = K - O + N - 1
let days = 354
if (MoslemLeapYear(y)) {
days++
}
if (JJ > days) {
JJ -= days
my++
}
let mm
let md
if (JJ === 355) {
mm = 12
md = 30
} else {
const S = base.floorDiv((JJ - 1) * 10, 295)
mm = 1 + S
md = base.floorDiv(10 * JJ - 295 * S, 10)
}
return { year: my, month: mm, day: md }
}
/**
* An MMonth specifies a month of the Moslum Calendar (Muharram = 1, ...).
*
* Upgraded to Unicode from the spellings given by Meeus.
* Source: http://en.wikipedia.org/wiki/Islamic_calendar.
*/
const 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'
]
/**
* String returns the Romanization of the month ("Muḥarram", "Ṣafar", ...).
*/
export function moslemMonth (m) {
return mmonths[m]
}
export default {
JewishCalendar,
MoslemToJD,
MoslemLeapYear,
JulianToMoslem,
moslemMonth
}