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_TERM_TABLE, 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'); const { Solar } = require('../lunar.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]; } } /** 计算某年某节气的公历日期(21世纪 C 值法,误差±1天) * termIndex 按天文顺序:小寒=0, 大寒=1, 立春=2, 雨水=3, ..., 冬至=23 */ function getSolarTermDate(year, termIndex) { // 21世纪 C 值表(小寒=0, 大寒=1, 立春=2 ... 冬至=23)已校准 const C21 = [ 5.11, 19.84, 3.6295, 18.4599, 5.3826, 20.4155, // 小寒 大寒 立春 雨水 惊蛰 春分 4.59, 19.888, 5.318, 20.86, 5.5, 21.20, // 清明 谷雨 立夏 小满 芒种 夏至 6.928, 22.65, 7.35, 22.95, 7.44, 22.822, // 小暑 大暑 立秋 处暑 白露 秋分 8.098, 23.218, 7.218, 22.08, 6.9, 21.60 // 寒露 霜降 立冬 小雪 大雪 冬至 ]; // 20世纪 C 值表 const C20 = [ 6.9, 21.37, 5.4055, 20.12, 7.1082, 22.83, 6.38, 21.646, 7.108, 22.36, 7.5, 23.13, 8.318, 24.64, 9.35, 24.77, 9.74, 24.643, 9.878, 24.918, 9.11, 23.65, 8.8, 23.54 ]; const Y = year % 100; const C = year >= 2000 ? C21[termIndex] : C20[termIndex]; // 闰年修正:小寒、大寒、立春、雨水(1-2月) let leapAdjust = 0; if (termIndex <= 3 && isLeapYear(year)) leapAdjust = 1; // 特殊修正 if (year === 2026 && termIndex === 3) leapAdjust = -1; // 2026雨水 if (year === 2084 && termIndex === 3) leapAdjust = 1; if (year === 1911 && termIndex === 6) leapAdjust = 1; // 清明 if (year === 1984 && termIndex === 7) leapAdjust = -1; // 谷雨 if (year === 1911 && termIndex === 8) leapAdjust = 1; // 立夏 if (year === 1981 && termIndex === 10) leapAdjust = -1; // 芒种 if (year === 1902 && termIndex === 11) leapAdjust = 1; // 夏至 if (year === 1928 && termIndex === 12) leapAdjust = 1; // 小暑 if (year === 1925 && termIndex === 13) leapAdjust = -1; // 大暑 if (year === 2002 && termIndex === 14) leapAdjust = 1; // 立秋 if (year === 1927 && termIndex === 16) leapAdjust = 1; // 白露 if (year === 1942 && termIndex === 17) leapAdjust = 1; // 秋分 if (year === 2089 && termIndex === 18) leapAdjust = 1; // 寒露 if (year === 2089 && termIndex === 19) leapAdjust = 1; // 霜降 if (year === 1978 && termIndex === 20) leapAdjust = 1; // 立冬 if (year === 1954 && termIndex === 21) leapAdjust = -1; // 小雪 if (year === 1918 && termIndex === 22) leapAdjust = 1; // 大雪 if (year === 2021 && termIndex === 22) leapAdjust = -1; if (year === 1902 && termIndex === 23) leapAdjust = 1; // 冬至 return Math.floor(Y * 0.2422 + C) - Math.floor(Y / 4) + leapAdjust; } /** 获取节气(优先精确查表 2000-2060,范围外用 C 值法兜底) * SOLAR_TERMS 数组从冬至开始:冬至=0, 小寒=1, 大寒=2, 立春=3, ..., 大雪=23 */ function getSolarTerm(year, month, day) { // month 月的两个节气在 SOLAR_TERMS 中的索引: // 1月→小寒(1)、大寒(2);2月→立春(3)、雨水(4);...;12月→大雪(23)、冬至(0) const idx1 = month === 12 ? 23 : (month * 2 - 1); // 每月第一个节气 const idx2 = month === 12 ? 0 : (month * 2); // 每月第二个节气 // 转为天文顺序(小寒=0):SOLAR_TERMS 索引 - 1(冬至特殊为23) const astroIdx1 = idx1 === 0 ? 23 : idx1 - 1; const astroIdx2 = idx2 === 0 ? 23 : idx2 - 1; // 优先用精确表 const table = SOLAR_TERM_TABLE[year]; if (table) { if (day === table[astroIdx1]) return SOLAR_TERMS[idx1]; if (day === table[astroIdx2]) return SOLAR_TERMS[idx2]; return null; } // 兜底:C 值法 const d1 = getSolarTermDate(year, astroIdx1); const d2 = getSolarTermDate(year, astroIdx2); if (day === d1) return SOLAR_TERMS[idx1]; if (day === d2) return SOLAR_TERMS[idx2]; 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 pengZu = getPengZu(dayGz); const clashHarm = getClashHarmCombine(dayGz.branch); const nayin = getNayin(dayGz.ganzhi); const hourDetails = getHourlyAlmanac(dayGz); // 用 lunar-javascript 获取真实的宜忌/吉神/凶煞(老黄历数据,最多 20+ 项) const lunar = Solar.fromYmd(year, month, day).getLunar(); const recommends = lunar.getDayYi(); const avoids = lunar.getDayJi(); const goodGods = lunar.getDayJiShen(); const badGods = lunar.getDayXiongSha(); 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 个节日(含基准日之后的) * baseYear/baseMonth/baseDay 可选,默认今天 * includeBuddhist 为 true 时,把佛教节日也并入一起按倒计时排序 */ function getUpcomingFestivals(count = 6, baseYear, baseMonth, baseDay, includeBuddhist = false) { const now = new Date(); const y = baseYear || now.getFullYear(); const m = baseMonth || (now.getMonth() + 1); const d = baseDay || 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, type: fest.type, 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; } } } // 佛历节日(开启后并入,统一按倒计时排序) if (includeBuddhist) { for (const [key, name] of Object.entries(BUDDHIST_FESTIVALS)) { 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, type: 'buddhist', 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 getYearFestivals(year) { const list = []; for (const fest of FESTIVAL_QUICK_SEARCH) { const solar = getFestivalSolarDate(year, fest); if (solar) { list.push({ name: fest.name, type: fest.type, solarDate: `${solar.year}-${String(solar.month).padStart(2, '0')}-${String(solar.day).padStart(2, '0')}`, solarYear: solar.year, solarMonth: solar.month, solarDay: solar.day }); } } // 按日期排序 list.sort((a, b) => { if (a.solarMonth !== b.solarMonth) return a.solarMonth - b.solarMonth; return a.solarDay - b.solarDay; }); return list; } /** 获取下一个佛教节日及倒计时(基准日可选,默认今天) */ function getNextBuddhistFestival(baseYear, baseMonth, baseDay) { const now = new Date(); const y = baseYear || now.getFullYear(); const m = baseMonth || (now.getMonth() + 1); const d = baseDay || 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, getYearFestivals, 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 };