Files
lunar-mini/mini/utils/core/index.js
T
gouki 82c1a8d694
Publish Mini Program Dev Version / publish (push) Successful in 2m8s
首页老黄历全面升级 + 修复农历转换核心bug
【重要修复】重写农历双向转换算法(原来全错):
- solarToLunar/lunarToSolar 改用标准算法(基准1900-01-31正月初一,UTC避免时区误差)
- 验证:2026春节2-17、中秋9-25、端午6-19、2023闰二月全部正确

【首页大日历】
- 日期数字加大到240rpx,超醒目
- 节假日红色喜庆卡片+红数字(春节/国庆/中秋等法定假日)
- 工作日绿色卡片+绿数字(一眼知道要上班)
- 周末红色数字+米黄卡片
- 节气显示具体时刻(如 立秋 20:29)
- 初一/十五金色标记(可在设置关闭)
- 佛历提醒:距下一佛诞还有几天(可在设置开启)

【全屏放大弹窗(老年友好)】
- 大日历卡片/宜忌/详情/节日速查 点击均可全屏放大
- 宜忌放大后字号44rpx,详情放大后40rpx

【月历视图】
- swiper 左右滑动换月,预计算5个月居中显示
- 滑到边缘自动补月,可无限滚动
- 点选日期切回日视图

【节日速查】
- 首页显示近期5个节日(中秋/重阳等)+公历日期+倒计时
- 点击直接跳到该日老黄历

【设置页】
- 新增'显示佛历提醒'开关(默认关)
- 新增'初一十五高亮'开关(默认开)
2026-08-07 12:49:57 +00:00

1385 lines
42 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
const {
LUNAR_INFO,
HEAVEN_STEMS,
EARTH_BRANCHES,
ZODIACS,
LUNAR_MONTH_NAMES,
LUNAR_DAY_NAMES,
WEEK_NAMES,
CONSTELLATIONS,
CONSTELLATION_DATES,
SOLAR_TERMS,
SOLAR_TERM_DATES,
SOLAR_FESTIVALS,
LUNAR_FESTIVALS,
BUDDHIST_FESTIVALS,
LEGAL_HOLIDAYS_SOLAR,
LEGAL_HOLIDAYS_LUNAR,
SOLAR_TERM_TIMES,
FESTIVAL_QUICK_SEARCH,
NAYIN_TABLE,
HOUR_NAMES,
HOUR_RANGES,
DUTY_NAMES,
TWELVE_STARS,
TWENTY_EIGHT_STARS,
NINE_STARS,
SIX_STARS,
PENG_ZU,
STEM_ELEMENTS,
BRANCH_ELEMENTS,
STEM_YINYANG,
BRANCH_YINYANG,
HIDE_STEMS,
TERRAINS,
BRANCH_COMBINE,
BRANCH_OPPOSITE,
BRANCH_HARM,
BRANCH_PUNISH,
THREE_COMBINES,
STEM_COMBINE,
STEM_OPPOSITE,
GENERATES,
KILLS,
TRIGRAMS,
HEXAGRAMS,
YEAR_WEIGHTS,
MONTH_WEIGHTS,
DAY_WEIGHTS,
HOUR_WEIGHTS,
BONE_INTERPRETATIONS
} = require('./data.js');
/** 判断是否为闰年 */
function isLeapYear(year) {
return (year % 4 === 0 && year % 100 !== 0) || year % 400 === 0;
}
/** 获取公历某月天数 */
function getSolarMonthDays(year, month) {
const days = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
if (month === 2 && isLeapYear(year)) return 29;
return days[month - 1];
}
/** 获取农历年天数 */
function getLunarYearDays(year) {
let sum = 348; // 12个月,每月29天
const info = LUNAR_INFO[year - 1900];
for (let i = 0x8000; i > 0x8; i >>= 1) {
if (info & i) sum += 1;
}
return sum + getLeapMonthDays(year);
}
/** 获取闰月月份(0表示无闰月) */
function getLeapMonth(year) {
return LUNAR_INFO[year - 1900] & 0xf;
}
/** 获取闰月天数 */
function getLeapMonthDays(year) {
if (getLeapMonth(year) === 0) return 0;
return (LUNAR_INFO[year - 1900] & 0x10000) ? 30 : 29;
}
/** 获取农历某月天数 */
function getLunarMonthDays(year, month) {
return (LUNAR_INFO[year - 1900] & (0x10000 >> month)) ? 30 : 29;
}
/** 公历转农历(标准算法,基准 1900-01-31 = 农历1900年正月初一) */
function solarToLunar(year, month, day) {
if (year < 1900 || year > 2100) return null;
// 与基准日 1900-01-31(农历1900年正月初一)的天数差(用 UTC 避免时区误差)
const baseDate = Date.UTC(1900, 0, 31);
const objDate = Date.UTC(year, month - 1, day);
let offset = Math.floor((objDate - baseDate) / 86400000);
let lunarYear = 1900;
let daysOfYear = 0;
// 确定农历年
for (; lunarYear < 2101 && offset > 0; lunarYear++) {
daysOfYear = getLunarYearDays(lunarYear);
if (offset < daysOfYear) break;
offset -= daysOfYear;
}
const leapMonth = getLeapMonth(lunarYear);
let isLeap = false;
let lunarMonth = 1;
let daysOfMonth = 0;
// 确定农历月日
for (; lunarMonth < 13 && offset > 0; lunarMonth++) {
// 闰月
if (leapMonth > 0 && lunarMonth === leapMonth + 1 && !isLeap) {
lunarMonth--;
isLeap = true;
daysOfMonth = getLeapMonthDays(lunarYear);
} else {
daysOfMonth = getLunarMonthDays(lunarYear, lunarMonth);
}
if (offset < daysOfMonth) break;
offset -= daysOfMonth;
// 解除闰月标记
if (isLeap && lunarMonth === leapMonth + 1) isLeap = false;
}
// offset 为 0 且恰好是闰月边界
if (offset === 0 && leapMonth > 0 && lunarMonth === leapMonth + 1) {
if (isLeap) {
isLeap = false;
} else {
isLeap = true;
lunarMonth--;
}
}
if (offset < 0) {
offset += daysOfMonth;
lunarMonth--;
}
return {
year: lunarYear,
month: lunarMonth,
day: offset + 1,
isLeap
};
}
/** 农历转公历(标准算法,基准 1900-01-31 = 农历1900年正月初一) */
function lunarToSolar(year, month, day, isLeap = false) {
if (year < 1900 || year > 2100) return null;
// 累计从 1900 年正月初一到目标农历日期的天数
let offset = 0;
for (let y = 1900; y < year; y++) {
offset += getLunarYearDays(y);
}
const leapMonth = getLeapMonth(year);
// 累计当年 1 月到目标月前一月的 days
for (let m = 1; m < month; m++) {
offset += getLunarMonthDays(year, m);
// 经过闰月(在 m 月之后)
if (m === leapMonth) {
offset += getLeapMonthDays(year);
}
}
// 目标月本身是闰月:需先加上该月正常月天数
if (isLeap && month === leapMonth) {
offset += getLunarMonthDays(year, month);
}
offset += day - 1;
// 基准日 1900-01-31 + offset
const baseMs = Date.UTC(1900, 0, 31);
const target = new Date(baseMs + offset * 86400000);
return {
year: target.getUTCFullYear(),
month: target.getUTCMonth() + 1,
day: target.getUTCDate()
};
}
/** 获取日期的干支 */
function getGanzhi(year, month, day) {
// 以1900年1月1日为甲子日(实际上1900-01-01是甲戌日,这里用近似算法)
// 更准确的算法:以某个已知日期为基准
const baseDate = new Date(1900, 0, 31); // 1900-01-31 是甲子日
const targetDate = new Date(year, month - 1, day);
const diffDays = Math.floor((targetDate - baseDate) / (24 * 60 * 60 * 1000));
const stemIndex = (diffDays % 10 + 10) % 10;
const branchIndex = (diffDays % 12 + 12) % 12;
return {
stem: HEAVEN_STEMS[stemIndex],
branch: EARTH_BRANCHES[branchIndex],
ganzhi: HEAVEN_STEMS[stemIndex] + EARTH_BRANCHES[branchIndex]
};
}
/** 获取年的干支 */
function getYearGanzhi(year) {
const stemIndex = (year - 4) % 10;
const branchIndex = (year - 4) % 12;
return {
stem: HEAVEN_STEMS[stemIndex],
branch: EARTH_BRANCHES[branchIndex],
ganzhi: HEAVEN_STEMS[stemIndex] + EARTH_BRANCHES[branchIndex]
};
}
/** 获取月的干支(以节气划分) */
function getMonthGanzhi(year, month, day) {
// 简化算法:以立春为正月起点
const yearGz = getYearGanzhi(year);
const yearStemIndex = HEAVEN_STEMS.indexOf(yearGz.stem);
// 月干 = (年干 * 2 + 月数) % 10
const monthStemIndex = (yearStemIndex * 2 + month) % 10;
// 月支固定:正月寅,二月卯...
const monthBranchIndex = (month + 1) % 12;
return {
stem: HEAVEN_STEMS[monthStemIndex],
branch: EARTH_BRANCHES[monthBranchIndex],
ganzhi: HEAVEN_STEMS[monthStemIndex] + EARTH_BRANCHES[monthBranchIndex]
};
}
/** 获取时辰的干支 */
function getHourGanzhi(dayGanzhi, hour) {
const dayStemIndex = HEAVEN_STEMS.indexOf(dayGanzhi.stem);
const hourBranchIndex = Math.floor(((hour + 1) % 24) / 2);
// 时干 = (日干 * 2 + 时支) % 10
const hourStemIndex = (dayStemIndex * 2 + hourBranchIndex) % 10;
return {
stem: HEAVEN_STEMS[hourStemIndex],
branch: EARTH_BRANCHES[hourBranchIndex],
ganzhi: HEAVEN_STEMS[hourStemIndex] + EARTH_BRANCHES[hourBranchIndex]
};
}
/** 获取生肖 */
function getZodiac(year) {
return ZODIACS[(year - 4) % 12];
}
/** 获取星座 */
function getConstellation(month, day) {
const dates = CONSTELLATION_DATES;
const index = month - 1;
if (day < dates[index]) {
return CONSTELLATIONS[index];
} else {
return CONSTELLATIONS[index + 1];
}
}
/** 获取节气 */
function getSolarTerm(year, month, day) {
const termDates = SOLAR_TERM_DATES[month - 1];
if (day === termDates[0]) {
return SOLAR_TERMS[(month - 1) * 2];
} else if (day === termDates[1]) {
return SOLAR_TERMS[(month - 1) * 2 + 1];
}
return null;
}
/** 获取纳音 */
function getNayin(ganzhi) {
const stemIndex = HEAVEN_STEMS.indexOf(ganzhi[0]);
const branchIndex = EARTH_BRANCHES.indexOf(ganzhi[1]);
const index = Math.floor(stemIndex / 2) * 6 + Math.floor(branchIndex / 2);
return NAYIN_TABLE[index % 30];
}
/** 获取建除十二神 */
function getDuty(monthGanzhi, dayGanzhi) {
const monthBranchIndex = EARTH_BRANCHES.indexOf(monthGanzhi.branch);
const dayBranchIndex = EARTH_BRANCHES.indexOf(dayGanzhi.branch);
const dutyIndex = (dayBranchIndex - monthBranchIndex + 12) % 12;
return DUTY_NAMES[dutyIndex];
}
/** 获取黄道黑道 */
function getTwelveStar(dayGanzhi) {
const branchIndex = EARTH_BRANCHES.indexOf(dayGanzhi.branch);
const starIndex = branchIndex % 12;
const star = TWELVE_STARS[starIndex];
const ecliptic = ['青龙', '明堂', '金匮', '天德', '玉堂', '司命'].includes(star) ? '黄道' : '黑道';
return { name: star, ecliptic, luck: ecliptic === '黄道' ? 'good' : 'bad' };
}
/** 获取二十八宿 */
function getTwentyEightStar(year, month, day) {
// 简化算法:以某已知日期为基准
const baseDate = new Date(1900, 0, 1);
const targetDate = new Date(year, month - 1, day);
const diffDays = Math.floor((targetDate - baseDate) / (24 * 60 * 60 * 1000));
const index = diffDays % 28;
const star = TWENTY_EIGHT_STARS[(index + 28) % 28];
return {
name: star,
luck: Math.random() > 0.5 ? 'good' : 'bad', // 简化
animal: star.slice(-1)
};
}
/** 获取九星 */
function getNineStar(year, month, day) {
const baseDate = new Date(1900, 0, 1);
const targetDate = new Date(year, month - 1, day);
const diffDays = Math.floor((targetDate - baseDate) / (24 * 60 * 60 * 1000));
const index = diffDays % 9;
return {
name: NINE_STARS[(index + 9) % 9],
color: '',
element: ''
};
}
/** 获取六曜 */
function getSixStar(lunarMonth, lunarDay) {
const index = (lunarMonth + lunarDay) % 6;
return SIX_STARS[index];
}
/** 获取小六壬 */
function getMinorRen(lunarMonth, lunarDay, hour) {
const index = (lunarMonth + lunarDay + Math.floor(hour / 2)) % 6;
const names = ['大安', '留连', '速喜', '赤口', '小吉', '空亡'];
const lucks = ['good', 'bad', 'good', 'bad', 'good', 'bad'];
const elements = ['木', '土', '火', '金', '水', '土'];
return {
name: names[index],
luck: lucks[index],
element: elements[index]
};
}
/** 获取月相 */
function getMoonPhase(lunarDay) {
if (lunarDay === 1) return '朔月';
if (lunarDay <= 7) return '上弦月';
if (lunarDay === 15) return '满月';
if (lunarDay <= 22) return '下弦月';
return '残月';
}
/** 获取胎神方位 */
function getFetus(dayGanzhi) {
const directions = ['东', '南', '西', '北', '中'];
const sides = ['房内', '房外'];
const index = HEAVEN_STEMS.indexOf(dayGanzhi.stem) % 5;
return {
direction: directions[index],
side: sides[index % 2],
position: `${directions[index]}${sides[index % 2]}`
};
}
/** 获取宜忌 */
function getRecommendsAvoids(duty) {
const recommendsMap = {
'建': ['出行', '上任', '会友', '上书', '见工'],
'除': ['除服', '疗病', '出行', '拆卸', '入宅'],
'满': ['嫁娶', '祈福', '移徙', '开市', '交易'],
'平': ['嫁娶', '修造', '动土', '竖柱', '上梁'],
'定': ['嫁娶', '祈福', '求嗣', '开光', '出行'],
'执': ['祭祀', '祈福', '求嗣', '开光', '嫁娶'],
'破': ['破屋', '坏垣', '求医', '治病'],
'危': ['祭祀', '祈福', '求嗣', '斋醮', '嫁娶'],
'成': ['嫁娶', '开市', '交易', '立券', '纳财'],
'收': ['纳财', '捕捉', '取渔', '纳畜'],
'开': ['嫁娶', '祈福', '求嗣', '开光', '出行'],
'闭': ['祭祀', '祈福', '求嗣', '斋醮', '安葬']
};
const avoidsMap = {
'建': ['动土', '开仓', '掘井', '开渠'],
'除': ['嫁娶', '开市', '交易', '立券'],
'满': ['安葬', '行丧', '伐木', '作梁'],
'平': ['开市', '交易', '立券', '纳财'],
'定': ['诉讼', '出行', '交涉'],
'执': ['开市', '交易', '立券', '纳财'],
'破': ['嫁娶', '开市', '交易', '立券', '纳财'],
'危': ['登高', '行船', '出行'],
'成': ['诉讼', '争讼'],
'收': ['嫁娶', '开市', '交易', '立券'],
'开': ['安葬', '行丧'],
'闭': ['嫁娶', '开市', '交易', '立券', '纳财']
};
return {
recommends: recommendsMap[duty] || [],
avoids: avoidsMap[duty] || []
};
}
/** 获取吉神凶神 */
function getGods(dayGanzhi) {
// 简化实现
const goodGods = ['天德', '月德', '天恩', '母仓', '时阳', '生气', '益后', '青龙'];
const badGods = ['劫煞', '灾煞', '月煞', '月刑', '月害', '月厌', '大时', '大败'];
return { goodGods, badGods };
}
/** 获取冲煞 */
function getClashHarmCombine(dayBranch) {
const clash = BRANCH_OPPOSITE[dayBranch];
const harm = BRANCH_HARM[dayBranch];
const combine = BRANCH_COMBINE[dayBranch];
const zodiacMap = {};
EARTH_BRANCHES.forEach((b, i) => { zodiacMap[b] = ZODIACS[i]; });
return {
clash: clash ? `${zodiacMap[clash]}(${clash})` : '',
harm: harm ? `${zodiacMap[harm]}(${harm})` : '',
combine: combine ? `${zodiacMap[combine]}(${combine})` : '',
evilDirection: ['北', '南', '东', '西'][EARTH_BRANCHES.indexOf(dayBranch) % 4]
};
}
/** 获取彭祖百忌 */
function getPengZu(dayGanzhi) {
const stemIndex = HEAVEN_STEMS.indexOf(dayGanzhi.stem);
const branchIndex = EARTH_BRANCHES.indexOf(dayGanzhi.branch);
return {
text: PENG_ZU.stems[stemIndex] + ' ' + PENG_ZU.branches[branchIndex],
stem: PENG_ZU.stems[stemIndex],
branch: PENG_ZU.branches[branchIndex]
};
}
/** 获取时辰黄历 */
function getHourlyAlmanac(dayGanzhi) {
const result = [];
for (let i = 0; i < 12; i++) {
const hourGz = getHourGanzhi(dayGanzhi, i * 2);
result.push({
branch: EARTH_BRANCHES[i],
name: HOUR_NAMES[i],
range: HOUR_RANGES[i],
ganzhi: hourGz.ganzhi,
recommends: [],
avoids: [],
twelveStar: TWELVE_STARS[i % 12],
nineStar: NINE_STARS[i % 9]
});
}
return result;
}
/** 获取日信息 */
function getDayInfo(year, month, day) {
const date = new Date(year, month - 1, day);
const weekDay = date.getDay();
const lunar = solarToLunar(year, month, day);
const dayGz = getGanzhi(year, month, day);
const yearGz = getYearGanzhi(year);
const monthGz = getMonthGanzhi(year, month, day);
const solarTerm = getSolarTerm(year, month, day);
const now = new Date();
const isToday = year === now.getFullYear() && month === now.getMonth() + 1 && day === now.getDate();
return {
solarDate: `${year}-${String(month).padStart(2, '0')}-${String(day).padStart(2, '0')}`,
solarDay: day,
solarMonth: month,
solarYear: year,
weekDay: WEEK_NAMES[weekDay],
weekDayIndex: weekDay,
constellation: getConstellation(month, day),
solarTerm,
solarTermTime: null,
isTermDay: !!solarTerm,
currentSolarTerm: null,
season: ['冬季', '冬季', '春季', '春季', '春季', '夏季', '夏季', '夏季', '秋季', '秋季', '秋季', '冬季'][month - 1],
termDayIndex: null,
nextSolarTerm: null,
daysToNextTerm: null,
julianDay: null,
buddhistYear: year + 543,
hijriDate: null,
buddhistFestival: lunar ? BUDDHIST_FESTIVALS[`${lunar.month}-${lunar.day}`] || null : null,
phenology: null,
dogDay: null,
nineDay: null,
lunarYear: lunar ? lunar.year : 0,
lunarMonth: lunar ? lunar.month : 0,
lunarMonthName: lunar ? LUNAR_MONTH_NAMES[lunar.month - 1] : '',
lunarDay: lunar ? lunar.day : 0,
lunarDayName: lunar ? LUNAR_DAY_NAMES[lunar.day - 1] : '',
isLeapMonth: lunar ? lunar.isLeap : false,
lunarYearGanzhi: yearGz.ganzhi,
lunarMonthGanzhi: monthGz.ganzhi,
lunarDayGanzhi: dayGz.ganzhi,
zodiac: getZodiac(year),
lunarFestival: lunar ? LUNAR_FESTIVALS[`${lunar.month}-${lunar.day}`] || null : null,
solarFestival: SOLAR_FESTIVALS[`${month}-${day}`] || null,
legalHoliday: null,
moonPhase: lunar ? getMoonPhase(lunar.day) : null,
isToday,
isWeekend: weekDay === 0 || weekDay === 6,
dayOfWeek: weekDay
};
}
/** 获取今日信息 */
function getTodayInfo() {
const now = new Date();
return getDayInfo(now.getFullYear(), now.getMonth() + 1, now.getDate());
}
/** 获取月历 */
function getMonthCalendar(year, month, weekStart = 0) {
const firstDay = new Date(year, month - 1, 1);
const lastDay = new Date(year, month, 0);
const daysInMonth = lastDay.getDate();
const firstWeekDay = firstDay.getDay();
const weeks = [];
let currentWeek = [];
// 填充上月天数
const prevMonthDays = weekStart === 0 ? firstWeekDay : (firstWeekDay + 6) % 7;
if (prevMonthDays > 0) {
const prevMonth = month === 1 ? 12 : month - 1;
const prevYear = month === 1 ? year - 1 : year;
const prevMonthLastDay = getSolarMonthDays(prevYear, prevMonth);
for (let i = prevMonthDays - 1; i >= 0; i--) {
const d = prevMonthLastDay - i;
currentWeek.push(getDayInfo(prevYear, prevMonth, d));
}
}
// 填充当月天数
for (let d = 1; d <= daysInMonth; d++) {
currentWeek.push(getDayInfo(year, month, d));
if (currentWeek.length === 7) {
weeks.push(currentWeek);
currentWeek = [];
}
}
// 填充下月天数
if (currentWeek.length > 0) {
const nextMonth = month === 12 ? 1 : month + 1;
const nextYear = month === 12 ? year + 1 : year;
let d = 1;
while (currentWeek.length < 7) {
currentWeek.push(getDayInfo(nextYear, nextMonth, d));
d++;
}
weeks.push(currentWeek);
}
return weeks;
}
/** 获取黄历信息 */
function getAlmanacInfo(year, month, day) {
const dayInfo = getDayInfo(year, month, day);
const dayGz = { stem: dayInfo.lunarDayGanzhi[0], branch: dayInfo.lunarDayGanzhi[1], ganzhi: dayInfo.lunarDayGanzhi };
const monthGz = { stem: dayInfo.lunarMonthGanzhi[0], branch: dayInfo.lunarMonthGanzhi[1], ganzhi: dayInfo.lunarMonthGanzhi };
const duty = getDuty(monthGz, dayGz);
const twelveStar = getTwelveStar(dayGz);
const twentyEightStar = getTwentyEightStar(year, month, day);
const nineStar = getNineStar(year, month, day);
const sixStar = getSixStar(dayInfo.lunarMonth, dayInfo.lunarDay);
const minorRen = getMinorRen(dayInfo.lunarMonth, dayInfo.lunarDay, 12);
const phase = dayInfo.moonPhase || '';
const fetus = getFetus(dayGz);
const { recommends, avoids } = getRecommendsAvoids(duty);
const { goodGods, badGods } = getGods(dayGz);
const pengZu = getPengZu(dayGz);
const clashHarm = getClashHarmCombine(dayGz.branch);
const nayin = getNayin(dayGz.ganzhi);
const hourDetails = getHourlyAlmanac(dayGz);
const luckyDuties = ['除', '执', '危', '成', '开'];
const unluckyDuties = ['建', '满', '平', '破', '收', '闭'];
let dutyLuck = 'neutral';
if (luckyDuties.includes(duty)) dutyLuck = 'good';
else if (unluckyDuties.includes(duty)) dutyLuck = 'bad';
return {
duty,
dutyLuck,
twelveStar,
twentyEightStar,
nineStar,
sixStar,
minorRen,
phase,
fetus,
recommends,
avoids,
goodGods,
badGods,
dayStem: dayGz.stem,
dayBranch: dayGz.branch,
dayGanzhi: dayGz.ganzhi,
pengZu: pengZu.text,
pengZuStem: pengZu.stem,
pengZuBranch: pengZu.branch,
clash: clashHarm.clash,
harm: clashHarm.harm,
combine: clashHarm.combine,
evilDirection: clashHarm.evilDirection,
nayin,
hourDetails
};
}
/** 八字排盘 */
function birthInfoToBazi(params) {
const { year, month, day, hour, minute, gender, ziSect = 'lateZiNextDay' } = params;
// 处理晚子时
let y = year, m = month, d = day;
if (ziSect === 'lateZiNextDay' && hour >= 23) {
const nextDay = new Date(year, month - 1, day + 1);
y = nextDay.getFullYear();
m = nextDay.getMonth() + 1;
d = nextDay.getDate();
}
const dayInfo = getDayInfo(y, m, d);
const dayGz = { stem: dayInfo.lunarDayGanzhi[0], branch: dayInfo.lunarDayGanzhi[1], ganzhi: dayInfo.lunarDayGanzhi };
const yearGz = { stem: dayInfo.lunarYearGanzhi[0], branch: dayInfo.lunarYearGanzhi[1], ganzhi: dayInfo.lunarYearGanzhi };
const monthGz = { stem: dayInfo.lunarMonthGanzhi[0], branch: dayInfo.lunarMonthGanzhi[1], ganzhi: dayInfo.lunarMonthGanzhi };
const hourGz = getHourGanzhi(dayGz, hour);
function extractPillarInfo(gz) {
const stem = gz.stem;
const branch = gz.branch;
const hideStems = (HIDE_STEMS[branch] || []).map(h => ({
stem: h.stem,
type: h.type,
tenStar: null
}));
const terrainIndex = (HEAVEN_STEMS.indexOf(stem) * 12 + EARTH_BRANCHES.indexOf(branch)) % 12;
const terrainName = TERRAINS[terrainIndex];
const goodTerrain = ['长生', '冠带', '临官', '帝旺', '胎', '养'];
const badTerrain = ['死', '墓', '绝'];
let terrainFortune = 'neutral';
if (goodTerrain.includes(terrainName)) terrainFortune = 'good';
else if (badTerrain.includes(terrainName)) terrainFortune = 'bad';
return {
ganzhi: gz.ganzhi,
heavenStem: stem,
earthBranch: branch,
elementStem: STEM_ELEMENTS[stem],
elementBranch: BRANCH_ELEMENTS[branch],
yinYangStem: STEM_YINYANG[stem],
yinYangBranch: BRANCH_YINYANG[branch],
hideStems,
nayin: getNayin(gz.ganzhi),
terrain: { name: terrainName, fortune: terrainFortune },
tenStar: null
};
}
const yearPillar = extractPillarInfo(yearGz);
const monthPillar = extractPillarInfo(monthGz);
const dayPillar = extractPillarInfo(dayGz);
const hourPillar = extractPillarInfo(hourGz);
// 计算十神(相对于日主)
const dayStemIndex = HEAVEN_STEMS.indexOf(dayGz.stem);
function getTenStar(stem) {
const stemIndex = HEAVEN_STEMS.indexOf(stem);
const diff = (stemIndex - dayStemIndex + 10) % 10;
const stars = ['比肩', '劫财', '食神', '伤官', '偏财', '正财', '七杀', '正官', '偏印', '正印'];
return stars[diff];
}
yearPillar.tenStar = getTenStar(yearGz.stem);
monthPillar.tenStar = getTenStar(monthGz.stem);
hourPillar.tenStar = getTenStar(hourGz.stem);
// 藏干十神
[yearPillar, monthPillar, dayPillar, hourPillar].forEach(p => {
p.hideStems.forEach(h => {
h.tenStar = getTenStar(h.stem);
});
});
// 胎元、胎息、命宫、身宫(简化)
const fetalOrigin = monthGz.ganzhi;
const fetalBreath = dayGz.ganzhi;
const ownSign = '命宫';
const bodySign = '身宫';
// 空亡
const emptyBranches = [];
const dayBranchIndex = EARTH_BRANCHES.indexOf(dayGz.branch);
const emptyIndex1 = (dayBranchIndex + 10) % 12;
const emptyIndex2 = (dayBranchIndex + 11) % 12;
emptyBranches.push(EARTH_BRANCHES[emptyIndex1], EARTH_BRANCHES[emptyIndex2]);
// 起运(简化)
const childLimit = {
startTime: `${year}-${String(month).padStart(2, '0')}-${String(day).padStart(2, '0')} ${String(hour).padStart(2, '0')}:${String(minute).padStart(2, '0')}:00`,
endTime: '',
yearCount: 8,
monthCount: 0,
dayCount: 0,
hourCount: 0,
minuteCount: 0,
forward: gender === 'male',
startAge: 8,
endAge: 88
};
// 大运(简化,10条)
const decadeFortunes = [];
for (let i = 0; i < 10; i++) {
const stemIndex = (HEAVEN_STEMS.indexOf(monthGz.stem) + i + 1) % 10;
const branchIndex = (EARTH_BRANCHES.indexOf(monthGz.branch) + i + 1) % 12;
const ganzhi = HEAVEN_STEMS[stemIndex] + EARTH_BRANCHES[branchIndex];
decadeFortunes.push({
index: i,
ganzhi,
startAge: 8 + i * 10,
endAge: 17 + i * 10,
startYear: year + 8 + i * 10,
endYear: year + 17 + i * 10,
heavenStem: HEAVEN_STEMS[stemIndex],
earthBranch: EARTH_BRANCHES[branchIndex],
nayin: getNayin(ganzhi)
});
}
// 流年(简化,10条)
const annualFortunes = [];
for (let i = 0; i < 10; i++) {
const y = year + i;
const yGz = getYearGanzhi(y);
annualFortunes.push({
age: i + 1,
year: y,
ganzhi: yGz.ganzhi,
nayin: getNayin(yGz.ganzhi)
});
}
return {
eightChar: {
birthDate: `${year}-${String(month).padStart(2, '0')}-${String(day).padStart(2, '0')}`,
birthTime: `${String(hour).padStart(2, '0')}:${String(minute).padStart(2, '0')}`,
gender,
yearPillar,
monthPillar,
dayPillar,
hourPillar,
dayMaster: dayGz.stem + STEM_ELEMENTS[dayGz.stem],
dayMasterStem: dayGz.stem,
dayMasterElement: STEM_ELEMENTS[dayGz.stem],
fetalOrigin,
fetalBreath,
ownSign,
bodySign,
emptyBranches,
emptyTen: ''
},
childLimit,
decadeFortunes,
annualFortunes
};
}
/** 获取地支关系 */
function getBranchRelationship(branchA, branchB) {
const combine = BRANCH_COMBINE[branchA] === branchB;
const opposite = BRANCH_OPPOSITE[branchA] === branchB;
const harm = BRANCH_HARM[branchA] === branchB;
let threeCombine = false;
let formation = null;
for (const [name, branches] of Object.entries(THREE_COMBINES)) {
if (branches.includes(branchA) && branches.includes(branchB)) {
threeCombine = true;
formation = name;
break;
}
}
let punish = false;
for (const [a, b] of BRANCH_PUNISH) {
if ((a === branchA && b === branchB) || (a === branchB && b === branchA)) {
punish = true;
break;
}
}
return { combine, threeCombine, opposite, harm, punish, formation };
}
/** 获取十神关系 */
function getTenStarRelationship(subjectStem, objectStem) {
const sIndex = HEAVEN_STEMS.indexOf(subjectStem);
const oIndex = HEAVEN_STEMS.indexOf(objectStem);
const diff = (oIndex - sIndex + 10) % 10;
const stars = ['比肩', '劫财', '食神', '伤官', '偏财', '正财', '七杀', '正官', '偏印', '正印'];
return stars[diff];
}
/** 检查天干合 */
function checkStemCombine(stemA, stemB) {
return STEM_COMBINE[stemA] === stemB;
}
/** 检查天干冲 */
function checkStemOpposite(stemA, stemB) {
return STEM_OPPOSITE[stemA] === stemB;
}
/** 计算每日运势 */
function calculateDailyFortune(userBazi, date) {
const year = date.getFullYear();
const month = date.getMonth() + 1;
const day = date.getDate();
const dayInfo = getDayInfo(year, month, day);
const dayGz = { stem: dayInfo.lunarDayGanzhi[0], branch: dayInfo.lunarDayGanzhi[1], ganzhi: dayInfo.lunarDayGanzhi };
const pillars = ['year', 'month', 'day', 'hour'];
const pillarLabels = { year: '年柱', month: '月柱', day: '日柱', hour: '时柱' };
const relationships = [];
for (const key of pillars) {
const userPillar = userBazi[`${key}Pillar`];
const dayP = key === 'year' ? { stem: dayInfo.lunarYearGanzhi[0], branch: dayInfo.lunarYearGanzhi[1] } :
key === 'month' ? { stem: dayInfo.lunarMonthGanzhi[0], branch: dayInfo.lunarMonthGanzhi[1] } :
key === 'day' ? dayGz : { stem: dayGz.stem, branch: dayGz.branch };
const stemTenStar = getTenStarRelationship(userPillar.heavenStem, dayP.stem);
const stemCombine = checkStemCombine(userPillar.heavenStem, dayP.stem);
const stemOpposite = checkStemOpposite(userPillar.heavenStem, dayP.stem);
const branchRel = getBranchRelationship(userPillar.earthBranch, dayP.branch);
let score = 0;
if (stemTenStar) {
const good = ['正印', '偏印', '食神', '正财', '偏财', '正官'];
const bad = ['七杀', '劫财', '伤官'];
if (good.includes(stemTenStar)) score += 4;
else if (bad.includes(stemTenStar)) score -= 3;
else score += 1;
}
if (stemCombine) score += 5;
if (stemOpposite) score -= 6;
if (branchRel.combine) score += 4;
if (branchRel.threeCombine) score += 3;
if (branchRel.opposite) score -= 5;
if (branchRel.harm) score -= 4;
if (branchRel.punish) score -= 3;
score = Math.max(-10, Math.min(10, score));
relationships.push({
pillar: key,
pillarLabel: pillarLabels[key],
userGanzhi: userPillar.ganzhi,
dayGanzhi: dayP.stem + dayP.branch,
stemTenStar,
stemCombine,
stemOpposite,
branchCombine: branchRel.combine,
branchThreeCombine: branchRel.threeCombine,
branchOpposite: branchRel.opposite,
branchHarm: branchRel.harm,
branchPunish: branchRel.punish,
branchFormation: branchRel.formation,
score
});
}
const weightedScore = relationships[0].score * 0.20 + relationships[1].score * 0.25 +
relationships[2].score * 0.40 + relationships[3].score * 0.15;
const overallScore = Math.max(-100, Math.min(100, Math.round(weightedScore * 10)));
const scoreLevel = overallScore >= 50 ? 'great' : overallScore >= 20 ? 'good' :
overallScore >= -20 ? 'fair' : overallScore >= -50 ? 'poor' : 'bad';
const luckyAspects = [];
const unluckyAspects = [];
const suggestions = [];
const affectedAreas = ['综合运势'];
// 简化生成
if (overallScore >= 20) {
luckyAspects.push('今天运势不错,适合推进重要事项');
suggestions.push('✅ 适合推进重要事项,果断决策会有好结果');
} else if (overallScore <= -20) {
unluckyAspects.push('今天运势低迷,宜保守行事');
suggestions.push('🛡️ 宜静不宜动重要决策能缓则缓');
} else {
luckyAspects.push('今天整体运势平稳');
suggestions.push('💡 运势平稳该做什么就做什么');
}
const de = userBazi.dayMasterElement;
const colorMap = { '木': ['绿色', '青色'], '火': ['红色', '紫色'], '土': ['黄色', '棕色'], '金': ['白色', '银色'], '水': ['蓝色', '黑色'] };
const dirMap = { '木': '东方', '火': '南方', '土': '中央', '金': '西方', '水': '北方' };
const colors = colorMap[de] || ['红色', '白色'];
const direction = dirMap[de] || '东方';
return {
date: `${year}-${String(month).padStart(2, '0')}-${String(day).padStart(2, '0')}`,
lunarDate: `${dayInfo.lunarYear}${dayInfo.lunarMonthName}${dayInfo.lunarDayName}`,
dayGanzhi: dayGz.ganzhi,
overallScore,
scoreLevel,
pillarRelationships: relationships,
luckyAspects,
unluckyAspects,
suggestions,
affectedAreas,
luckyMeta: {
colors,
numbers: [3, 7],
direction,
element: de,
activity: '阅读'
},
categoryScores: {
love: overallScore,
career: overallScore,
wealth: overallScore,
health: overallScore
}
};
}
/** 五行分析 */
function analyzeElementBalance(bazi) {
const pillars = [bazi.yearPillar, bazi.monthPillar, bazi.dayPillar, bazi.hourPillar];
const counts = { '木': 0, '火': 0, '土': 0, '金': 0, '水': 0 };
for (const pillar of pillars) {
if (pillar.elementStem) counts[pillar.elementStem] += 1;
if (pillar.elementBranch) counts[pillar.elementBranch] += 1;
for (const hs of pillar.hideStems) {
const el = STEM_ELEMENTS[hs.stem];
if (el) counts[el] += 0.5;
}
}
const total = Object.values(counts).reduce((a, b) => a + b, 0);
let dominant = '木', weakest = '木';
let maxCount = 0, minCount = Infinity;
for (const [el, count] of Object.entries(counts)) {
if (count > maxCount) { maxCount = count; dominant = el; }
if (count < minCount) { minCount = count; weakest = el; }
}
const ideal = total / 5;
let isBalanced = true;
for (const count of Object.values(counts)) {
if (Math.abs(count - ideal) > ideal * 0.4) {
isBalanced = false;
break;
}
}
return {
wood: counts['木'],
fire: counts['火'],
earth: counts['土'],
metal: counts['金'],
water: counts['水'],
total,
dominant,
weakest,
isBalanced
};
}
/** 梅花易数 */
function calculatePlumBlossom(year, month, day, hour = 12) {
const yearNum = year;
const monthNum = month;
const dayNum = day;
const hourIndex = Math.floor(((hour + 1) % 24) / 2);
const upperIdx = ((yearNum + monthNum + dayNum) % 8) + 1;
const lowerIdx = ((monthNum + dayNum + hourIndex + 1) % 8) + 1;
const changingLine = ((yearNum + monthNum + dayNum + hourIndex + 1) % 6) + 1;
const upperTrigram = TRIGRAMS[upperIdx];
const lowerTrigram = TRIGRAMS[lowerIdx];
const originalKey = `${upperIdx},${lowerIdx}`;
const originalHexagram = HEXAGRAMS[originalKey] || {
number: (upperIdx - 1) * 8 + lowerIdx,
name: upperTrigram.name + lowerTrigram.name,
upperTrigram,
lowerTrigram,
changingLine: 0,
interpretation: '此卦象需结合具体事理参详',
judgment: '',
image: '',
lines: ['', '', '', '', '', '']
};
// 变卦
let transUpperIdx = upperIdx;
let transLowerIdx = lowerIdx;
if (changingLine >= 4) {
transUpperIdx = flipTrigramLine(upperIdx, changingLine - 3);
} else {
transLowerIdx = flipTrigramLine(lowerIdx, changingLine);
}
const transKey = `${transUpperIdx},${transLowerIdx}`;
const transformedHexagram = HEXAGRAMS[transKey] || null;
// 互卦(简化)
const mutualKey = `${lowerIdx},${upperIdx}`;
const mutualHexagram = HEXAGRAMS[mutualKey] || null;
const constitution = lowerTrigram.element;
const function_ = upperTrigram.element;
const relationship = getElementRelationship(constitution, function_);
return {
originalHexagram: { ...originalHexagram, changingLine },
transformedHexagram,
mutualHexagram,
upperTrigram,
lowerTrigram,
changingLine,
constitution,
function: function_,
relationship
};
}
function flipTrigramLine(trigramIdx, line) {
const encoding = [0, 7, 6, 5, 4, 3, 2, 1, 0];
let bits = encoding[trigramIdx] || 0;
bits ^= (1 << (line - 1));
const decoding = [8, 7, 6, 2, 5, 3, 4, 1];
return decoding[bits] || trigramIdx;
}
function getElementRelationship(body, func) {
if (body === func) return '比和(体用相同,诸事顺利)';
if (GENERATES[body] === func) return '体生用(泄气,宜守不宜攻)';
if (GENERATES[func] === body) return '用生体(得力,有贵人相助)';
if (KILLS[func] === body) return '用克体(受制,诸事不顺)';
if (KILLS[body] === func) return '体克用(主动,需付出努力)';
return '体用相生';
}
/** 称骨算命 */
function calculateBoneWeight(lunarYearGanzhiIndex, lunarMonth, lunarDay, earthBranchHourIndex) {
const yearWt = YEAR_WEIGHTS[lunarYearGanzhiIndex] || 9;
const monthWt = MONTH_WEIGHTS[lunarMonth] || 9;
const dayWt = DAY_WEIGHTS[lunarDay] || 9;
const hourWt = HOUR_WEIGHTS[earthBranchHourIndex] || 9;
const totalWeight = yearWt + monthWt + dayWt + hourWt;
const totalLiang = Math.floor(totalWeight / 10);
const totalQian = totalWeight % 10;
const interpretation = BONE_INTERPRETATIONS[totalWeight] || { text: '命格推来,自有天定', fortune: '中' };
return {
yearWeight: yearWt,
monthWeight: monthWt,
dayWeight: dayWt,
hourWeight: hourWt,
totalWeight,
totalLiang,
totalQian,
interpretation: interpretation.text,
fortune: interpretation.fortune
};
}
/** 获取佛教节日 */
function getBuddhistFestival(lunarMonth, lunarDay) {
return BUDDHIST_FESTIVALS[`${lunarMonth}-${lunarDay}`] || null;
}
/** 判断是否为法定节假日 */
function getLegalHoliday(year, month, day) {
const solarKey = `${month}-${day}`;
if (LEGAL_HOLIDAYS_SOLAR[solarKey]) return LEGAL_HOLIDAYS_SOLAR[solarKey];
const lunar = solarToLunar(year, month, day);
if (lunar) {
const lunarKey = `${lunar.month}-${lunar.day}`;
if (LEGAL_HOLIDAYS_LUNAR[lunarKey]) return LEGAL_HOLIDAYS_LUNAR[lunarKey];
}
return null;
}
/** 计算某天是星期几(0=周日) */
function getWeekDay(year, month, day) {
return new Date(year, month - 1, day).getDay();
}
/** 判断是否为工作日(周一到周五且非法定节假日) */
function isWorkday(year, month, day) {
if (getLegalHoliday(year, month, day)) return false;
const wd = getWeekDay(year, month, day);
return wd >= 1 && wd <= 5;
}
/** 农历转公历(某年) */
function lunarFestivalToSolar(year, lunarMonth, lunarDay) {
// 除夕特殊:腊月最后一天
if (lunarMonth === 12 && lunarDay === 30) {
const daysInMonth = getLunarMonthDays(year, 12);
const leap = getLeapMonth(year);
if (leap === 12) {
return lunarToSolar(year, 12, daysInMonth, true);
}
return lunarToSolar(year, 12, daysInMonth, false);
}
return lunarToSolar(year, lunarMonth, lunarDay, false);
}
/** 计算某农历节日在公历 year 年的日期 */
function getFestivalSolarDate(year, fest) {
if (fest.type === 'solar') {
const [m, d] = fest.date.split('-').map(Number);
return { year, month: m, day: d };
}
const [lm, ld] = fest.date.split('-').map(Number);
// 农历节日可能落在上一公历年(如春节在1-2月)
// 先按当前公历年算
let solar = lunarFestivalToSolar(year, lm, ld);
if (!solar) return null;
return solar;
}
/** 计算两个日期相差天数(target - from */
function daysBetween(fromY, fromM, fromD, toY, toM, toD) {
const a = new Date(fromY, fromM - 1, fromD);
const b = new Date(toY, toM - 1, toD);
return Math.round((b - a) / (24 * 60 * 60 * 1000));
}
/** 获取即将到来的 N 个节日(含今天之后的) */
function getUpcomingFestivals(count = 6) {
const now = new Date();
const y = now.getFullYear();
const m = now.getMonth() + 1;
const d = now.getDate();
const list = [];
for (const fest of FESTIVAL_QUICK_SEARCH) {
// 尝试今年和明年
for (const tryYear of [y, y + 1]) {
const solar = getFestivalSolarDate(tryYear, fest);
if (!solar) continue;
const diff = daysBetween(y, m, d, solar.year, solar.month, solar.day);
if (diff >= 0) {
list.push({
name: fest.name,
solarDate: `${solar.year}-${String(solar.month).padStart(2, '0')}-${String(solar.day).padStart(2, '0')}`,
solarYear: solar.year,
solarMonth: solar.month,
solarDay: solar.day,
daysLeft: diff
});
break;
}
}
}
list.sort((a, b) => a.daysLeft - b.daysLeft);
return list.slice(0, count);
}
/** 获取下一个佛教节日及倒计时 */
function getNextBuddhistFestival() {
const now = new Date();
const y = now.getFullYear();
const m = now.getMonth() + 1;
const d = now.getDate();
const entries = Object.entries(BUDDHIST_FESTIVALS);
const list = [];
for (const [key, name] of entries) {
const [lm, ld] = key.split('-').map(Number);
for (const tryYear of [y, y + 1]) {
const solar = lunarFestivalToSolar(tryYear, lm, ld);
if (!solar) continue;
const diff = daysBetween(y, m, d, solar.year, solar.month, solar.day);
if (diff >= 0) {
list.push({ name, solarDate: `${solar.year}-${String(solar.month).padStart(2, '0')}-${String(solar.day).padStart(2, '0')}`, daysLeft: diff });
break;
}
}
}
list.sort((a, b) => a.daysLeft - b.daysLeft);
return list[0] || null;
}
/** 神煞分析 */
function analyzeShensha(bazi) {
const result = [];
const pillars = [bazi.yearPillar, bazi.monthPillar, bazi.dayPillar, bazi.hourPillar];
const dayStem = bazi.dayMasterStem;
const dayBranch = bazi.dayPillar.earthBranch;
// 天乙贵人
const tianYiMap = { '甲': ['丑', '未'], '戊': ['丑', '未'], '乙': ['子', '申'], '己': ['子', '申'], '丙': ['亥', '酉'], '丁': ['亥', '酉'], '壬': ['卯', '巳'], '癸': ['卯', '巳'], '辛': ['寅', '午'] };
const tianYiTargets = tianYiMap[dayStem] || [];
const tianYiFound = pillars.filter(p => tianYiTargets.includes(p.earthBranch));
if (tianYiFound.length > 0) {
result.push({
name: '天乙贵人',
type: '吉',
category: '贵人',
anchors: ['日干'],
foundIn: tianYiFound.map(p => p.earthBranch),
description: '最吉之神煞,主逢凶化吉、贵人相助'
});
}
// 桃花
const taoHuaMap = { '申': '酉', '子': '酉', '辰': '酉', '寅': '卯', '午': '卯', '戌': '卯', '巳': '午', '酉': '午', '丑': '午', '亥': '子', '卯': '子', '未': '子' };
const taoHuaTarget = taoHuaMap[dayBranch];
if (taoHuaTarget) {
const taoHuaFound = pillars.filter(p => p.earthBranch === taoHuaTarget);
if (taoHuaFound.length > 0) {
result.push({
name: '桃花(咸池)',
type: '中性',
category: '感情',
anchors: ['日支'],
foundIn: taoHuaFound.map(p => p.earthBranch),
description: '主异性缘、魅力与风流,利艺术才华'
});
}
}
return result;
}
/** 大运/流年分析 */
function analyzeFortuneGanzhi(ganzhi, dayStem, dayBranch) {
const stem = ganzhi[0];
const branch = ganzhi[1];
const tenStar = getTenStarRelationship(dayStem, stem);
const el = STEM_ELEMENTS[stem];
const de = STEM_ELEMENTS[dayStem];
let elementRelation;
if (!el || !de || el === de) elementRelation = '比和';
else if (GENERATES[el] === de) elementRelation = '生我';
else if (GENERATES[de] === el) elementRelation = '我生';
else if (KILLS[el] === de) elementRelation = '克我';
else elementRelation = '我克';
const stemCombine = checkStemCombine(stem, dayStem);
const stemOpposite = checkStemOpposite(stem, dayStem);
const br = getBranchRelationship(branch, dayBranch);
let score = 0;
const goodTenStars = ['正印', '偏印', '食神', '正财', '偏财', '正官'];
const badTenStars = ['七杀', '劫财', '伤官'];
if (tenStar) {
if (goodTenStars.includes(tenStar)) score += 4;
else if (badTenStars.includes(tenStar)) score -= 3;
else score += 1;
}
if (stemCombine) score += 5;
if (stemOpposite) score -= 6;
if (br.combine) score += 4;
if (br.threeCombine) score += 3;
if (br.opposite) score -= 5;
if (br.harm) score -= 4;
if (br.punish) score -= 3;
score = Math.max(-10, Math.min(10, score));
const level = score >= 3 ? '吉' : score <= -3 ? '凶' : '平';
return {
ganzhi,
tenStar,
elementRelation,
stemCombine,
stemOpposite,
branchCombine: br.combine,
branchThreeCombine: br.threeCombine,
branchOpposite: br.opposite,
branchHarm: br.harm,
branchPunish: br.punish,
score,
level
};
}
/** 获取流月 */
function getYearMonths(year) {
const months = [];
const jieIndices = [3, 5, 7, 9, 11, 13, 15, 17, 19, 21, 23, 1];
const monthNames = ['正月', '二月', '三月', '四月', '五月', '六月', '七月', '八月', '九月', '十月', '冬月', '腊月'];
for (let k = 0; k < 12; k++) {
const termYear = k === 11 ? year + 1 : year;
const termIndex = jieIndices[k];
const month = Math.floor(termIndex / 2) + 1;
const day = SOLAR_TERM_DATES[month - 1][termIndex % 2];
const dayInfo = getDayInfo(termYear, month, day);
months.push({
index: k,
name: monthNames[k],
solarMonth: month,
startDate: `${termYear}-${String(month).padStart(2, '0')}-${String(day).padStart(2, '0')}`,
endDate: '',
ganzhi: dayInfo.lunarMonthGanzhi
});
}
return months;
}
module.exports = {
getDayInfo,
getTodayInfo,
getMonthCalendar,
getAlmanacInfo,
birthInfoToBazi,
getBranchRelationship,
getTenStarRelationship,
checkStemCombine,
checkStemOpposite,
calculateDailyFortune,
analyzeElementBalance,
calculatePlumBlossom,
calculateBoneWeight,
getBuddhistFestival,
getLegalHoliday,
isWorkday,
getWeekDay,
getFestivalSolarDate,
getUpcomingFestivals,
getNextBuddhistFestival,
daysBetween,
analyzeShensha,
analyzeFortuneGanzhi,
getYearMonths,
solarToLunar,
lunarToSolar,
getGanzhi,
getYearGanzhi,
getMonthGanzhi,
getHourGanzhi,
getZodiac,
getConstellation,
getSolarTerm,
getNayin,
HEAVEN_STEMS,
EARTH_BRANCHES,
ZODIACS,
LUNAR_MONTH_NAMES,
LUNAR_DAY_NAMES,
WEEK_NAMES,
SOLAR_TERMS,
HOUR_NAMES,
HOUR_RANGES,
TRIGRAMS,
HEXAGRAMS
};