viet-lunar-calendar
Version:
Helper tool to convert Gregorian date to Vietnamese Lunar Date (default timezone is GMT+7)
241 lines (240 loc) • 11.7 kB
JavaScript
import { SOLAR_TERM, STEMS, ZODIAC_BRANCHES } from "./model";
export default class CalendarCalculator {
parseGregorianDate(date) {
return {
day: date.getDate(),
month: date.getMonth() + 1,
year: date.getFullYear(),
};
}
floorNumber(number) {
return Math.floor(number);
}
getDayNumber(date) {
const gregorianDate = this.parseGregorianDate(date);
const a = this.floorNumber((14 - gregorianDate.month) / 12);
let y = gregorianDate.year + 4800 - a;
let m = gregorianDate.month + 12 * a - 3;
let dayNumber;
dayNumber = gregorianDate.day +
this.floorNumber((153 * m + 2) / 5) +
365 * y +
this.floorNumber(y / 4) -
this.floorNumber(y / 100) +
this.floorNumber(y / 400) -
32045;
if (dayNumber < 2299161) {
dayNumber = gregorianDate.day +
this.floorNumber((153 * m + 2) / 5) +
365 * y +
this.floorNumber(y / 4) -
32083;
}
return dayNumber;
}
getKNumber(dayNumber) {
return this.floorNumber((dayNumber - 2415021.076998695) / 29.530588853);
}
newMoon(k) {
const t = k / 1236.85;
const t2 = t * t;
const t3 = t2 * t;
const dr = Math.PI / 180;
let jd1 = 2415020.75933 + 29.53058868 * k + 0.0001178 * t2 - 0.000000155 * t3;
jd1 = jd1 + 0.00033 * Math.sin((166.56 + 132.87 * t - 0.009173 * t2) * dr);
const m = 359.2242 + 29.10535608 * k - 0.0000333 * t2 - 0.00000347 * t3;
const mpr = 306.0253 + 385.81691806 * k + 0.0107306 * t2 + 0.00001236 * t3;
const F = 21.2964 + 390.67050646 * k - 0.0016528 * t2 - 0.00000239 * t3;
let c1 = (0.1734 - 0.000393 * t) * Math.sin(m * dr) +
0.0021 * Math.sin(2 * dr * m);
c1 = c1 - 0.4068 * Math.sin(mpr * dr) + 0.0161 * Math.sin(dr * 2 * mpr);
c1 = c1 - 0.0004 * Math.sin(dr * 3 * mpr);
c1 = c1 + 0.0104 * Math.sin(dr * 2 * F) - 0.0051 * Math.sin(dr * (m + mpr));
c1 = c1 -
0.0074 * Math.sin(dr * (m - mpr)) +
0.0004 * Math.sin(dr * (2 * F + m));
c1 = c1 -
0.0004 * Math.sin(dr * (2 * F - m)) -
0.0006 * Math.sin(dr * (2 * F + mpr));
c1 = c1 +
0.0010 * Math.sin(dr * (2 * F - mpr)) +
0.0005 * Math.sin(dr * (2 * mpr + m));
let deltat;
if (t < -11) {
deltat = 0.001 +
0.000839 * t +
0.0002261 * t2 -
0.00000845 * t3 -
0.000000081 * t * t3;
}
else {
deltat = -0.000278 + 0.000265 * t + 0.000262 * t2;
}
const jdNew = jd1 + c1 - deltat;
return jdNew;
}
getNewMoonDay(k, timeZone) {
return this.floorNumber(this.newMoon(k) + 0.5 + timeZone / 24);
}
sunLongitude(jdn) {
const t = (jdn - 2451545.0) / 36525;
const t2 = t * t;
const dr = Math.PI / 180;
const m = 357.52910 + 35999.05030 * t - 0.0001559 * t2 - 0.00000048 * t * t2;
const l0 = 280.46645 + 36000.76983 * t + 0.0003032 * t2;
let dl = (1.914600 - 0.004817 * t - 0.000014 * t2) * Math.sin(dr * m);
dl = dl +
(0.019993 - 0.000101 * t) * Math.sin(dr * 2 * m) +
0.000290 * Math.sin(dr * 3 * m);
let l = l0 + dl;
l = l * dr;
l = l - Math.PI * 2 * ((this.floorNumber(l / (Math.PI * 2))));
return l;
}
getSunLongitude(dayNumber, timeZone) {
const jdn = dayNumber - 0.5 - timeZone / 24;
return this.floorNumber(this.sunLongitude(jdn) / Math.PI * 6);
}
getLunarMonth11(year, timeZone) {
const lastDayOfYear = new Date(year, 11, 31);
;
const off = this.getDayNumber(lastDayOfYear) - 2415021;
const k = this.floorNumber(off / 29.530588853);
let nm = this.getNewMoonDay(k, timeZone);
const sunLong = this.getSunLongitude(nm, timeZone);
if (sunLong >= 9) {
nm = this.getNewMoonDay(k - 1, timeZone);
}
return nm;
}
getSolarTermId(dayNumber, timeZone) {
return this.floorNumber(this.sunLongitude(dayNumber - 0.5 - timeZone / 24.0) / Math.PI * 12);
}
getLunarDayStemBranch(dayNumber) {
var _a, _b, _c, _d, _e;
const stemIndex = (dayNumber + 3) % 10;
const branchIndex = (dayNumber + 2) % 12 == 0 ? 12 : (dayNumber + 2) % 12;
return {
stemIndex: stemIndex,
branchIndex: branchIndex,
branchEmoji: `${(_b = (_a = ZODIAC_BRANCHES === null || ZODIAC_BRANCHES === void 0 ? void 0 : ZODIAC_BRANCHES[branchIndex]) === null || _a === void 0 ? void 0 : _a["emoji"]) !== null && _b !== void 0 ? _b : ''}`,
say: `${(_c = STEMS === null || STEMS === void 0 ? void 0 : STEMS[stemIndex]) !== null && _c !== void 0 ? _c : ''} ${(_e = (_d = ZODIAC_BRANCHES === null || ZODIAC_BRANCHES === void 0 ? void 0 : ZODIAC_BRANCHES[branchIndex]) === null || _d === void 0 ? void 0 : _d["han_viet"]) !== null && _e !== void 0 ? _e : ''}`
};
}
getLunarMonthStemBranch(year, month) {
var _a, _b, _c, _d, _e;
const stemIndex = (year * 12 + month + 7) % 10;
const branchIndex = (month + 2) % 12 == 0 ? 12 : (month + 2) % 12;
return {
stemIndex: stemIndex,
branchIndex: branchIndex,
branchEmoji: `${(_b = (_a = ZODIAC_BRANCHES === null || ZODIAC_BRANCHES === void 0 ? void 0 : ZODIAC_BRANCHES[branchIndex]) === null || _a === void 0 ? void 0 : _a["emoji"]) !== null && _b !== void 0 ? _b : ''}`,
say: `${(_c = STEMS === null || STEMS === void 0 ? void 0 : STEMS[stemIndex]) !== null && _c !== void 0 ? _c : ''} ${(_e = (_d = ZODIAC_BRANCHES === null || ZODIAC_BRANCHES === void 0 ? void 0 : ZODIAC_BRANCHES[branchIndex]) === null || _d === void 0 ? void 0 : _d["han_viet"]) !== null && _e !== void 0 ? _e : ''}`
};
}
getLunarYearStemBranch(year) {
var _a, _b, _c, _d, _e;
const stemIndex = (year + 10) % 10;
const branchIndex = (year + 9) % 12 == 0 ? 12 : (year + 9) % 12;
return {
stemIndex: stemIndex,
branchIndex: branchIndex,
branchEmoji: `${(_b = (_a = ZODIAC_BRANCHES === null || ZODIAC_BRANCHES === void 0 ? void 0 : ZODIAC_BRANCHES[branchIndex]) === null || _a === void 0 ? void 0 : _a["emoji"]) !== null && _b !== void 0 ? _b : ''}`,
say: `${(_c = STEMS === null || STEMS === void 0 ? void 0 : STEMS[stemIndex]) !== null && _c !== void 0 ? _c : ''} ${(_e = (_d = ZODIAC_BRANCHES === null || ZODIAC_BRANCHES === void 0 ? void 0 : ZODIAC_BRANCHES[branchIndex]) === null || _d === void 0 ? void 0 : _d["han_viet"]) !== null && _e !== void 0 ? _e : ''}`
};
}
getLeapMonthOffset(a11, timeZone) {
const k = this.floorNumber((a11 - 2415021.076998695) / 29.530588853 + 0.5);
let last = 0;
let i = 1;
let arc = this.getSunLongitude(this.getNewMoonDay(k + i, timeZone), timeZone);
while (arc != last && i < 14) {
last = arc;
i += 1;
arc = this.getSunLongitude(this.getNewMoonDay(k + i, timeZone), timeZone);
}
return i - 1;
}
getZodiacHours(date) {
var _a, _b, _c, _d, _e, _f, _g;
const zodiacHourMap = [
"110100101100",
"001101001011",
"110011010010",
"101100110100",
"001011001101",
"010010110011"
];
const dayNumber = this.getDayNumber(date);
const startStem = (((dayNumber - 1) * 2) % 10) + 4;
const dayBranchIndex = (dayNumber + 2) % 12 == 0 ? 12 : (dayNumber + 2) % 12;
let zodiacHourStr = zodiacHourMap[(dayBranchIndex - 1) % 6];
let zodiacHours = [];
for (let i = 0; i < zodiacHourStr.length; i++) {
const stemIndex = (startStem + i) % 10;
const branchIndex = i + 1;
zodiacHours.push({
presentFrom: (_b = (_a = ZODIAC_BRANCHES === null || ZODIAC_BRANCHES === void 0 ? void 0 : ZODIAC_BRANCHES[branchIndex]) === null || _a === void 0 ? void 0 : _a["hour_from"]) !== null && _b !== void 0 ? _b : 0,
presentTo: (_d = (_c = ZODIAC_BRANCHES === null || ZODIAC_BRANCHES === void 0 ? void 0 : ZODIAC_BRANCHES[branchIndex]) === null || _c === void 0 ? void 0 : _c["hour_to"]) !== null && _d !== void 0 ? _d : 0,
stemIndex: stemIndex,
branchIndex: branchIndex,
say: `${(_e = STEMS === null || STEMS === void 0 ? void 0 : STEMS[stemIndex]) !== null && _e !== void 0 ? _e : ''} ${(_g = (_f = ZODIAC_BRANCHES === null || ZODIAC_BRANCHES === void 0 ? void 0 : ZODIAC_BRANCHES[branchIndex]) === null || _f === void 0 ? void 0 : _f["han_viet"]) !== null && _g !== void 0 ? _g : ''}`,
goldenHour: zodiacHourStr[i] == "1"
});
}
return zodiacHours;
}
getLunarDate(date, timeZone = 7.0) {
var _a, _b, _c, _d, _e, _f;
const gregorianDate = this.parseGregorianDate(date);
const dayNumber = this.getDayNumber(date);
const k = this.getKNumber(dayNumber);
let newMoonDayNumber = this.getNewMoonDay(k + 1, timeZone);
if (newMoonDayNumber > dayNumber) {
newMoonDayNumber = this.getNewMoonDay(k, timeZone);
}
let a11 = this.getLunarMonth11(gregorianDate.year, timeZone);
let b11 = a11;
let lunarYear;
if (a11 >= newMoonDayNumber) {
lunarYear = gregorianDate.year;
a11 = this.getLunarMonth11(gregorianDate.year - 1, timeZone);
}
else {
lunarYear = gregorianDate.year + 1;
b11 = this.getLunarMonth11(gregorianDate.year + 1, timeZone);
}
const lunarDay = dayNumber - newMoonDayNumber + 1;
const diff = this.floorNumber((newMoonDayNumber - a11) / 29);
let lunarLeap = 0;
let lunarMonth = diff + 11;
if (b11 - a11 > 365) {
const leapMonthDiff = this.getLeapMonthOffset(a11, timeZone);
if (diff >= leapMonthDiff) {
lunarMonth = diff + 10;
if (diff == leapMonthDiff) {
lunarLeap = 1;
}
}
}
if (lunarMonth > 12) {
lunarMonth = lunarMonth - 12;
}
if (lunarMonth >= 11 && diff < 4) {
lunarYear -= 1;
}
const solarTermId = this.getSolarTermId(dayNumber + 1, timeZone);
return {
day: lunarDay,
month: lunarMonth,
monthSay: (_b = (_a = ZODIAC_BRANCHES === null || ZODIAC_BRANCHES === void 0 ? void 0 : ZODIAC_BRANCHES[lunarMonth]) === null || _a === void 0 ? void 0 : _a["month_say"]) !== null && _b !== void 0 ? _b : '',
year: lunarYear,
leap: lunarLeap,
dayStemBranch: this.getLunarDayStemBranch(dayNumber),
monthStemBranch: this.getLunarMonthStemBranch(lunarYear, lunarMonth),
yearStemBranch: this.getLunarYearStemBranch(lunarYear),
solarTermHanViet: (_d = (_c = SOLAR_TERM === null || SOLAR_TERM === void 0 ? void 0 : SOLAR_TERM[solarTermId]) === null || _c === void 0 ? void 0 : _c['han_viet']) !== null && _d !== void 0 ? _d : '',
solarTermViet: (_f = (_e = SOLAR_TERM === null || SOLAR_TERM === void 0 ? void 0 : SOLAR_TERM[solarTermId]) === null || _e === void 0 ? void 0 : _e['viet']) !== null && _f !== void 0 ? _f : '',
};
}
}