fix: chinese-almanac 使用 lunar-javascript 库 - 修复农历日期计算错误

master
Eason (陈医生) 1 month ago
parent 32dd8bd75d
commit a13dc38246
  1. 95
      skills/chinese-almanac/almanac.js

@ -1,27 +1,44 @@
/** /**
* Chinese Almanac (黄历) Query Skill * Chinese Almanac (黄历) Query Skill
* 使用 Tavily API 查询每日黄历信息 * 使用 Tavily API 查询黄历 + lunar-javascript 计算农历
*/ */
const { Solar, Lunar } = require('lunar-javascript');
const TAVILY_API_KEY = process.env.TAVILY_API_KEY || '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'; const TAVILY_API_KEY = process.env.TAVILY_API_KEY || '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';
/**
* 计算农历日期使用专业库
* lunar-javascript 会自动处理时区直接传入日期即可
*/
function getLunarDate(jsDate) {
// 使用 local time 创建 Solar 对象,lunar-javascript 会正确处理
const solar = Solar.fromYmd(jsDate.getFullYear(), jsDate.getMonth() + 1, jsDate.getDate());
const lunar = solar.getLunar();
return {
lunarDate: lunar.toString(), // 如:二〇二六年正月初十
lunarDay: lunar.getDayInChinese(), // 如:初十
lunarMonth: lunar.getMonthInChinese(), // 如:正月
lunarYear: lunar.getYearInChinese() // 如:二〇二六
};
}
/** /**
* 查询黄历信息 * 查询黄历信息
*/ */
async function queryAlmanac(date) { async function queryAlmanac(date) {
let targetDate;
if (!date) { if (!date) {
const tomorrow = new Date(); targetDate = new Date();
tomorrow.setDate(tomorrow.getDate() + 1); targetDate.setDate(targetDate.getDate() + 1); // 明天
date = tomorrow.toISOString().split('T')[0]; } else {
targetDate = new Date(date);
} }
// 动态计算农历日期(基于 2026 年春节 2 月 17 日) // 使用专业库计算农历
const targetDate = date ? new Date(date) : new Date(); const lunarInfo = getLunarDate(targetDate);
const springFestival = new Date('2026-02-17');
const lunarDay = Math.floor((targetDate - springFestival) / (1000 * 60 * 60 * 24)) + 1;
const lunarDateStr = `农历正月初${lunarDay}`;
const query = `${date} 黄历 宜忌 ${lunarDateStr}`; const query = `${targetDate.toISOString().split('T')[0]} 黄历 宜忌 ${lunarInfo.lunarDate}`;
try { try {
const response = await fetch('https://api.tavily.com/search', { const response = await fetch('https://api.tavily.com/search', {
@ -43,12 +60,12 @@ async function queryAlmanac(date) {
} }
const data = await response.json(); const data = await response.json();
const almanacInfo = parseAlmanacData(data, date, lunarDateStr); const almanacInfo = parseAlmanacData(data, targetDate, lunarInfo);
return { return {
success: true, success: true,
date: date, date: targetDate.toISOString().split('T')[0],
lunarDate: lunarDateStr, lunarDate: lunarInfo.lunarDate,
weekday: almanacInfo.weekday, weekday: almanacInfo.weekday,
ganzhi: almanacInfo.ganzhi, ganzhi: almanacInfo.ganzhi,
yi: almanacInfo.yi, yi: almanacInfo.yi,
@ -59,7 +76,7 @@ async function queryAlmanac(date) {
return { return {
success: false, success: false,
error: error.message, error: error.message,
fallback: getFallbackAlmanac(date, lunarDateStr) fallback: getFallbackAlmanac(targetDate, lunarInfo)
}; };
} }
} }
@ -67,11 +84,11 @@ async function queryAlmanac(date) {
/** /**
* 解析黄历数据 * 解析黄历数据
*/ */
function parseAlmanacData(data, date, lunarDateStr) { function parseAlmanacData(data, date, lunarInfo) {
const result = { const result = {
lunarDate: lunarDateStr, lunarDate: lunarInfo.lunarDate,
weekday: getWeekday(date), weekday: getWeekday(date),
ganzhi: '丙午年 庚寅月 己巳日', ganzhi: '丙午年 庚寅月 ' + getDayGanZhi(date),
yi: ['作灶', '解除', '平治道涂', '余事勿取'], yi: ['作灶', '解除', '平治道涂', '余事勿取'],
ji: ['祈福', '安葬', '祭祀', '安门'], ji: ['祈福', '安葬', '祭祀', '安门'],
chong: '冲猪 煞东' chong: '冲猪 煞东'
@ -110,22 +127,40 @@ function parseAlmanacData(data, date, lunarDateStr) {
/** /**
* 获取星期 * 获取星期
*/ */
function getWeekday(dateStr) { function getWeekday(date) {
const date = new Date(dateStr);
const weekdays = ['日', '一', '二', '三', '四', '五', '六']; const weekdays = ['日', '一', '二', '三', '四', '五', '六'];
return `星期${weekdays[date.getDay()]}`; return `星期${weekdays[date.getDay()]}`;
} }
/**
* 获取日干支简化版
*/
function getDayGanZhi(date) {
// 2026-02-17 是己巳日
const baseDate = new Date('2026-02-17');
const ganzhi = ['甲子','乙丑','丙寅','丁卯','戊辰','己巳','庚午','辛未','壬申','癸酉',
'甲戌','乙亥','丙子','丁丑','戊寅','己卯','庚辰','辛巳','壬午','癸未',
'甲申','乙酉','丙戌','丁亥','戊子','己丑','庚寅','辛卯','壬辰','癸巳',
'甲午','乙未','丙申','丁酉','戊戌','己亥','庚子','辛丑','壬寅','癸卯',
'甲辰','乙巳','丙午','丁未','戊申','己酉','庚戌','辛亥','壬子','癸丑',
'甲寅','乙卯','丙辰','丁巳','戊午','己未','庚申','辛酉','壬戌','癸亥'];
const daysSince = Math.floor((date - baseDate) / (1000*60*60*24));
const baseIndex = 5; // 己巳是第 5 个(从 0 开始)
const index = (baseIndex + daysSince) % 60;
return ganzhi[index >= 0 ? index : 60 + index] + '日';
}
/** /**
* Fallback 数据 * Fallback 数据
*/ */
function getFallbackAlmanac(date, lunarDateStr) { function getFallbackAlmanac(date, lunarInfo) {
return { return {
success: true, success: true,
date: date, date: date.toISOString().split('T')[0],
lunarDate: lunarDateStr, lunarDate: lunarInfo.lunarDate,
weekday: getWeekday(date), weekday: getWeekday(date),
ganzhi: '丙午年 庚寅月 己巳日', ganzhi: '丙午年 庚寅月 ' + getDayGanZhi(date),
yi: ['作灶', '解除', '平治道涂', '余事勿取'], yi: ['作灶', '解除', '平治道涂', '余事勿取'],
ji: ['祈福', '安葬', '祭祀', '安门'], ji: ['祈福', '安葬', '祭祀', '安门'],
chong: '冲猪 煞东', chong: '冲猪 煞东',
@ -160,9 +195,15 @@ function formatAlmanac(almanac) {
// 测试 // 测试
if (require.main === module) { if (require.main === module) {
queryAlmanac('2026-02-24').then(result => { console.log('=== 黄历查询测试(使用 lunar-javascript 库)===\n');
console.log(formatAlmanac(result));
}); const arg = process.argv[2] || 'tomorrow';
queryAlmanac(arg === 'today' ? null : (arg === 'tomorrow' ? undefined : arg))
.then(result => {
console.log(formatAlmanac(result));
console.log('\n详细信息:');
console.log(`农历:${result.lunarDate}`);
});
} }
module.exports = { queryAlmanac, formatAlmanac }; module.exports = { queryAlmanac, formatAlmanac, getLunarDate };

Loading…
Cancel
Save