|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
import {AnyCalendarDate, Calendar} from '../types'; |
|
import {CalendarDate} from '../CalendarDate'; |
|
import {mod, Mutable} from '../utils'; |
|
|
|
const HEBREW_EPOCH = 347997; |
|
|
|
|
|
|
|
const HOUR_PARTS = 1080; |
|
const DAY_PARTS = 24 * HOUR_PARTS; |
|
|
|
|
|
|
|
|
|
const MONTH_DAYS = 29; |
|
const MONTH_FRACT = 12 * HOUR_PARTS + 793; |
|
const MONTH_PARTS = MONTH_DAYS * DAY_PARTS + MONTH_FRACT; |
|
|
|
function isLeapYear(year: number) { |
|
return mod(year * 7 + 1, 19) < 7; |
|
} |
|
|
|
|
|
|
|
function hebrewDelay1(year: number) { |
|
let months = Math.floor((235 * year - 234) / 19); |
|
let parts = 12084 + 13753 * months; |
|
let day = months * 29 + Math.floor(parts / 25920); |
|
|
|
if (mod(3 * (day + 1), 7) < 3) { |
|
day += 1; |
|
} |
|
|
|
return day; |
|
} |
|
|
|
|
|
function hebrewDelay2(year: number) { |
|
let last = hebrewDelay1(year - 1); |
|
let present = hebrewDelay1(year); |
|
let next = hebrewDelay1(year + 1); |
|
|
|
if (next - present === 356) { |
|
return 2; |
|
} |
|
|
|
if (present - last === 382) { |
|
return 1; |
|
} |
|
|
|
return 0; |
|
} |
|
|
|
function startOfYear(year: number) { |
|
return hebrewDelay1(year) + hebrewDelay2(year); |
|
} |
|
|
|
function getDaysInYear(year: number) { |
|
return startOfYear(year + 1) - startOfYear(year); |
|
} |
|
|
|
function getYearType(year: number) { |
|
let yearLength = getDaysInYear(year); |
|
|
|
if (yearLength > 380) { |
|
yearLength -= 30; |
|
} |
|
|
|
switch (yearLength) { |
|
case 353: |
|
return 0; |
|
case 354: |
|
return 1; |
|
case 355: |
|
return 2; |
|
} |
|
} |
|
|
|
function getDaysInMonth(year: number, month: number): number { |
|
|
|
if (month >= 6 && !isLeapYear(year)) { |
|
month++; |
|
} |
|
|
|
|
|
if (month === 4 || month === 7 || month === 9 || month === 11 || month === 13) { |
|
return 29; |
|
} |
|
|
|
let yearType = getYearType(year); |
|
|
|
|
|
if (month === 2) { |
|
return yearType === 2 ? 30 : 29; |
|
} |
|
|
|
|
|
if (month === 3) { |
|
return yearType === 0 ? 29 : 30; |
|
} |
|
|
|
|
|
if (month === 6) { |
|
return isLeapYear(year) ? 30 : 0; |
|
} |
|
|
|
return 30; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
export class HebrewCalendar implements Calendar { |
|
identifier = 'hebrew'; |
|
|
|
fromJulianDay(jd: number): CalendarDate { |
|
let d = jd - HEBREW_EPOCH; |
|
let m = (d * DAY_PARTS) / MONTH_PARTS; |
|
let year = Math.floor((19 * m + 234) / 235) + 1; |
|
let ys = startOfYear(year); |
|
let dayOfYear = Math.floor(d - ys); |
|
|
|
|
|
while (dayOfYear < 1) { |
|
year--; |
|
ys = startOfYear(year); |
|
dayOfYear = Math.floor(d - ys); |
|
} |
|
|
|
|
|
let month = 1; |
|
let monthStart = 0; |
|
while (monthStart < dayOfYear) { |
|
monthStart += getDaysInMonth(year, month); |
|
month++; |
|
} |
|
|
|
month--; |
|
monthStart -= getDaysInMonth(year, month); |
|
|
|
let day = dayOfYear - monthStart; |
|
return new CalendarDate(this, year, month, day); |
|
} |
|
|
|
toJulianDay(date: AnyCalendarDate) { |
|
let jd = startOfYear(date.year); |
|
for (let month = 1; month < date.month; month++) { |
|
jd += getDaysInMonth(date.year, month); |
|
} |
|
|
|
return jd + date.day + HEBREW_EPOCH; |
|
} |
|
|
|
getDaysInMonth(date: AnyCalendarDate): number { |
|
return getDaysInMonth(date.year, date.month); |
|
} |
|
|
|
getMonthsInYear(date: AnyCalendarDate): number { |
|
return isLeapYear(date.year) ? 13 : 12; |
|
} |
|
|
|
getDaysInYear(date: AnyCalendarDate): number { |
|
return getDaysInYear(date.year); |
|
} |
|
|
|
getYearsInEra(): number { |
|
|
|
return 9999; |
|
} |
|
|
|
getEras() { |
|
return ['AM']; |
|
} |
|
|
|
balanceYearMonth(date: Mutable<AnyCalendarDate>, previousDate: AnyCalendarDate) { |
|
|
|
if (previousDate.year !== date.year) { |
|
if (isLeapYear(previousDate.year) && !isLeapYear(date.year) && previousDate.month > 6) { |
|
date.month--; |
|
} else if (!isLeapYear(previousDate.year) && isLeapYear(date.year) && previousDate.month > 6) { |
|
date.month++; |
|
} |
|
} |
|
} |
|
} |
|
|