From d73a87709c802c45d4766f95c802f5f53771a8cd Mon Sep 17 00:00:00 2001 From: ddshi <8811906+ddshi@user.noreply.gitee.com> Date: Sat, 28 Feb 2026 10:14:32 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E5=AE=8C=E5=96=84=E4=B8=AD=E5=9B=BD?= =?UTF-8?q?=E8=8A=82=E5=81=87=E6=97=A5=E5=92=8C=E5=86=9C=E5=8E=86=E6=95=B0?= =?UTF-8?q?=E6=8D=AE=E7=B3=BB=E7=BB=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 修复 isHoliday 函数逻辑错误(农历日期比较方向错误) - 扩展节假日数据:添加更多中国传统节日(龙抬头、七夕、中元节、寒衣节、下元节、小年等) - 添加现代节日(情人节、妇女节、青年节、儿童节、建军节、教师节、圣诞节) - 新增 utils/lunar.ts 工具模块,提供: - 生肖和天干地支获取 - 二十四节气查询 - 传统节日查询 - 农历/公历节日判断 - 扩展 lunar-javascript 类型声明 Co-Authored-By: Claude Opus 4.6 --- src/constants/holidays.ts | 207 ++++++++++++++++++++++++------ src/types/lunar-javascript.d.ts | 23 ++++ src/utils/lunar.ts | 218 ++++++++++++++++++++++++++++++++ 3 files changed, 412 insertions(+), 36 deletions(-) create mode 100644 src/utils/lunar.ts diff --git a/src/constants/holidays.ts b/src/constants/holidays.ts index 0cd8b62..c8d4894 100644 --- a/src/constants/holidays.ts +++ b/src/constants/holidays.ts @@ -18,6 +18,7 @@ export interface Holiday { } export const HOLIDAYS: Holiday[] = [ + // ========== 公历节日 ========== // 元旦 { id: 'new-year', @@ -28,24 +29,24 @@ export const HOLIDAYS: Holiday[] = [ isStatutory: true, repeatYearly: true, }, - // 春节 + // 情人节 { - id: 'spring-festival', - name: '春节', - isLunar: true, - lunarMonth: 1, - lunarDay: 1, - isStatutory: true, + id: 'valentines-day', + name: '情人节', + month: 2, + day: 14, + isLunar: false, + isStatutory: false, repeatYearly: true, }, - // 元宵节 + // 妇女节 { - id: 'lantern', - name: '元宵节', - isLunar: true, - lunarMonth: 1, - lunarDay: 15, - isStatutory: false, + id: 'womens-day', + name: '妇女节', + month: 3, + day: 8, + isLunar: false, + isStatutory: true, repeatYearly: true, }, // 清明节 @@ -68,6 +69,108 @@ export const HOLIDAYS: Holiday[] = [ isStatutory: true, repeatYearly: true, }, + // 青年节 + { + id: 'youth-day', + name: '青年节', + month: 5, + day: 4, + isLunar: false, + isStatutory: true, + repeatYearly: true, + }, + // 儿童节 + { + id: 'childrens-day', + name: '儿童节', + month: 6, + day: 1, + isLunar: false, + isStatutory: true, + repeatYearly: true, + }, + // 建军节 + { + id: 'army-day', + name: '建军节', + month: 8, + day: 1, + isLunar: false, + isStatutory: true, + repeatYearly: true, + }, + // 教师节 + { + id: 'teachers-day', + name: '教师节', + month: 9, + day: 10, + isLunar: false, + isStatutory: false, + repeatYearly: true, + }, + // 国庆节 + { + id: 'national-day', + name: '国庆节', + month: 10, + day: 1, + isLunar: false, + isStatutory: true, + repeatYearly: true, + }, + // 圣诞节 + { + id: 'christmas', + name: '圣诞节', + month: 12, + day: 25, + isLunar: false, + isStatutory: false, + repeatYearly: true, + }, + // 冬至(使用固定公历日期) + { + id: 'winter-solstice', + name: '冬至', + month: 12, + day: 21, + isLunar: false, + isStatutory: false, + repeatYearly: true, + }, + + // ========== 农历节日 ========== + // 春节 + { + id: 'spring-festival', + name: '春节', + isLunar: true, + lunarMonth: 1, + lunarDay: 1, + isStatutory: true, + repeatYearly: true, + }, + // 元宵节 + { + id: 'lantern', + name: '元宵节', + isLunar: true, + lunarMonth: 1, + lunarDay: 15, + isStatutory: false, + repeatYearly: true, + }, + // 龙抬头(春耕节) + { + id: 'dragon-head', + name: '龙抬头', + isLunar: true, + lunarMonth: 2, + lunarDay: 2, + isStatutory: false, + repeatYearly: true, + }, // 端午节 { id: 'dragon-boat', @@ -78,6 +181,26 @@ export const HOLIDAYS: Holiday[] = [ isStatutory: true, repeatYearly: true, }, + // 七夕节 + { + id: 'Qixi', + name: '七夕节', + isLunar: true, + lunarMonth: 7, + lunarDay: 7, + isStatutory: false, + repeatYearly: true, + }, + // 中元节 + { + id: 'ghost', + name: '中元节', + isLunar: true, + lunarMonth: 7, + lunarDay: 15, + isStatutory: false, + repeatYearly: true, + }, // 中秋节 { id: 'mid-autumn', @@ -88,16 +211,6 @@ export const HOLIDAYS: Holiday[] = [ isStatutory: true, repeatYearly: true, }, - // 国庆节 - { - id: 'national-day', - name: '国庆节', - month: 10, - day: 1, - isLunar: false, - isStatutory: true, - repeatYearly: true, - }, // 重阳节 { id: 'double-ninth', @@ -108,13 +221,23 @@ export const HOLIDAYS: Holiday[] = [ isStatutory: false, repeatYearly: true, }, - // 冬至 + // 寒衣节 { - id: 'winter-solstice', - name: '冬至', - month: 12, - day: 21, - isLunar: false, + id: 'han-yi', + name: '寒衣节', + isLunar: true, + lunarMonth: 10, + lunarDay: 1, + isStatutory: false, + repeatYearly: true, + }, + // 下元节 + { + id: 'xia-yuan', + name: '下元节', + isLunar: true, + lunarMonth: 10, + lunarDay: 15, isStatutory: false, repeatYearly: true, }, @@ -128,6 +251,16 @@ export const HOLIDAYS: Holiday[] = [ isStatutory: false, repeatYearly: true, }, + // 小年 + { + id: 'little-year', + name: '小年', + isLunar: true, + lunarMonth: 12, + lunarDay: 23, + isStatutory: false, + repeatYearly: true, + }, // 除夕 { id: 'chinese-new-years-eve', @@ -156,6 +289,7 @@ export function getHolidayById(id: string): Holiday | undefined { /** * 检查给定日期是否为节假日 + * 正确逻辑:把农历节日转换成公历日期后比较 */ export function isHoliday( date: Date, @@ -166,13 +300,14 @@ export function isHoliday( for (const holiday of HOLIDAYS) { if (holiday.isLunar && holiday.lunarMonth && holiday.lunarDay) { - // 农历日期需要转换 + // 农历日期:把农历节日转换成公历日期再比较 try { - const lunar = Lunar.fromYmd(year, month, day); + const lunar = Lunar.fromYmd(year, holiday.lunarMonth, holiday.lunarDay); const solar = lunar.getSolar(); if ( - solar.getMonth() === holiday.lunarMonth && - solar.getDay() === holiday.lunarDay + solar.getMonth() === month && + solar.getDay() === day && + solar.getYear() === year ) { return holiday; } @@ -199,8 +334,8 @@ export function getHolidaysForYear(year: number): Array a.date.getTime() - b.date.getTime()); + return result; +} + +/** + * 获取指定日期的节气(如果有) + */ +export function getJieQiForDate(date: Date): string | null { + try { + const lunar = Lunar.fromYmd( + date.getFullYear(), + date.getMonth() + 1, + date.getDate() + ); + const jieQi = lunar.getPrevJieQi(); + if (jieQi) { + return jieQi.getName(); + } + } catch { + // 忽略错误 + } + return null; +} + +/** + * 获取农历年干支纪年 + */ +export function getYearGanZhi(year: number): string { + try { + const lunar = Lunar.fromYmd(year, 1, 1); + return lunar.getYearInGanZhi(); + } catch { + return ''; + } +} + +/** + * 获取农历月干支纪月 + */ +export function getMonthGanZhi(year: number, month: number): string { + try { + const lunar = Lunar.fromYmd(year, month, 1); + return lunar.getMonthInGanZhi(); + } catch { + return ''; + } +} + +/** + * 获取农历日干支纪日 + */ +export function getDayGanZhi(date: Date): string { + try { + const lunar = Lunar.fromYmd( + date.getFullYear(), + date.getMonth() + 1, + date.getDate() + ); + return lunar.getDayInGanZhi(); + } catch { + return ''; + } +} + +/** + * 判断是否为农历节日 + */ +export function isLunarFestival(date: Date): string | null { + try { + const lunar = Lunar.fromYmd( + date.getFullYear(), + date.getMonth() + 1, + date.getDate() + ); + const festival = lunar.getFestivals(); + if (festival && festival.length > 0) { + return festival[0]; + } + } catch { + // 忽略错误 + } + return null; +} + +/** + * 判断是否为公历节日 + */ +export function isSolarFestival(date: Date): string | null { + try { + const solar = Solar.fromYmd( + date.getFullYear(), + date.getMonth() + 1, + date.getDate() + ); + const festival = solar.getFestivals(); + if (festival && festival.length > 0) { + return festival[0]; + } + } catch { + // 忽略错误 + } + return null; +} + +/** + * 获取传统节日信息(扩展版本) + * 包含更多中国传统节日 + */ +export interface TraditionalFestival { + id: string; + name: string; + date: Date; // 该年的公历日期 + isLunar: boolean; // 是否为农历节日 + description?: string; // 节日描述 +} + +/** + * 获取年份的传统节日列表 + */ +export function getTraditionalFestivals(year: number): TraditionalFestival[] { + const festivals: TraditionalFestival[] = []; + + // 使用 lunar-javascript 的节假日功能 + try { + const holidays = LunarHoliday.fromYear(year); + for (const holiday of holidays) { + const solar = holiday.getSolar(); + festivals.push({ + id: holiday.getName(), + name: holiday.getName(), + date: new Date(solar.getYear(), solar.getMonth() - 1, solar.getDay()), + isLunar: true, + description: holiday.getName(), + }); + } + } catch { + // 忽略错误 + } + + // 按日期排序 + festivals.sort((a, b) => a.date.getTime() - b.date.getTime()); + return festivals; +}