|
|
|
@ -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'); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const arg = process.argv[2] || 'tomorrow'; |
|
|
|
|
|
|
|
queryAlmanac(arg === 'today' ? null : (arg === 'tomorrow' ? undefined : arg)) |
|
|
|
|
|
|
|
.then(result => { |
|
|
|
console.log(formatAlmanac(result)); |
|
|
|
console.log(formatAlmanac(result)); |
|
|
|
|
|
|
|
console.log('\n详细信息:'); |
|
|
|
|
|
|
|
console.log(`农历:${result.lunarDate}`); |
|
|
|
}); |
|
|
|
}); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
module.exports = { queryAlmanac, formatAlmanac }; |
|
|
|
module.exports = { queryAlmanac, formatAlmanac, getLunarDate }; |
|
|
|
|