feat: 完善中国节假日和农历数据系统
- 修复 isHoliday 函数逻辑错误(农历日期比较方向错误) - 扩展节假日数据:添加更多中国传统节日(龙抬头、七夕、中元节、寒衣节、下元节、小年等) - 添加现代节日(情人节、妇女节、青年节、儿童节、建军节、教师节、圣诞节) - 新增 utils/lunar.ts 工具模块,提供: - 生肖和天干地支获取 - 二十四节气查询 - 传统节日查询 - 农历/公历节日判断 - 扩展 lunar-javascript 类型声明 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
8725108195
commit
d73a87709c
@ -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<Holiday & { date: Date }
|
||||
for (const holiday of HOLIDAYS) {
|
||||
if (holiday.isLunar && holiday.lunarMonth && holiday.lunarDay) {
|
||||
try {
|
||||
// 使用 Lunar 构造函数创建农历对象,再获取对应的公历日期
|
||||
const lunar = new Lunar(year, holiday.lunarMonth, holiday.lunarDay);
|
||||
// 使用 Lunar.fromYmd 创建农历对象,再获取对应的公历日期
|
||||
const lunar = Lunar.fromYmd(year, holiday.lunarMonth, holiday.lunarDay);
|
||||
const solar = lunar.getSolar();
|
||||
result.push({
|
||||
...holiday,
|
||||
|
||||
23
src/types/lunar-javascript.d.ts
vendored
23
src/types/lunar-javascript.d.ts
vendored
@ -4,13 +4,36 @@ declare module 'lunar-javascript' {
|
||||
getSolar(): Solar;
|
||||
getMonth(): number;
|
||||
getDay(): number;
|
||||
getYear(): number;
|
||||
getMonthInChinese(): string;
|
||||
getDayInChinese(): string;
|
||||
isLeapMonth(): boolean;
|
||||
getYearInChinese(): string;
|
||||
getZodiac(): string;
|
||||
getYearInGanZhi(): string;
|
||||
getMonthInGanZhi(): string;
|
||||
getDayInGanZhi(): string;
|
||||
getFestivals(): string[];
|
||||
getPrevJieQi(): JieQi | null;
|
||||
getLunar(): Lunar;
|
||||
}
|
||||
|
||||
export class Solar {
|
||||
static fromYmd(year: number, month: number, day: number): Solar;
|
||||
getYear(): number;
|
||||
getMonth(): number;
|
||||
getDay(): number;
|
||||
getFestivals(): string[];
|
||||
}
|
||||
|
||||
export class JieQi {
|
||||
getName(): string;
|
||||
getSolar(): Solar;
|
||||
}
|
||||
|
||||
export class Holiday {
|
||||
static fromYear(year: number): Holiday[];
|
||||
getName(): string;
|
||||
getSolar(): Solar;
|
||||
}
|
||||
}
|
||||
|
||||
218
src/utils/lunar.ts
Normal file
218
src/utils/lunar.ts
Normal file
@ -0,0 +1,218 @@
|
||||
/**
|
||||
* 农历工具函数
|
||||
* 提供更丰富的中国传统文化日期信息
|
||||
*/
|
||||
import { Lunar, Solar, Holiday as LunarHoliday } from 'lunar-javascript';
|
||||
|
||||
/**
|
||||
* 获取公历日期的农历信息
|
||||
*/
|
||||
export interface LunarInfo {
|
||||
year: number; // 农历年份
|
||||
month: number; // 农历月份
|
||||
day: number; // 农历日期
|
||||
monthInChinese: string; // 农历月份中文
|
||||
dayInChinese: string; // 农历日期中文
|
||||
isLeapMonth: boolean; // 是否闰月
|
||||
yearInChinese: string; // 农历年(甲子年等)
|
||||
zodiac: string; // 生肖
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取二十四节气信息
|
||||
*/
|
||||
export interface JieQiInfo {
|
||||
name: string; // 节气名称
|
||||
nameInChinese: string; // 节气中文名
|
||||
date: Date; // 节气日期
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取指定公历日期的农历信息
|
||||
*/
|
||||
export function getLunarInfo(date: Date): LunarInfo {
|
||||
const lunar = Lunar.fromYmd(
|
||||
date.getFullYear(),
|
||||
date.getMonth() + 1,
|
||||
date.getDate()
|
||||
);
|
||||
|
||||
return {
|
||||
year: lunar.getYear(),
|
||||
month: lunar.getMonth(),
|
||||
day: lunar.getDay(),
|
||||
monthInChinese: lunar.getMonthInChinese(),
|
||||
dayInChinese: lunar.getDayInChinese(),
|
||||
isLeapMonth: lunar.isLeapMonth(),
|
||||
yearInChinese: lunar.getYearInChinese(),
|
||||
zodiac: lunar.getZodiac(),
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取指定年份的二十四节气
|
||||
*/
|
||||
export function getJieQiForYear(year: number): JieQiInfo[] {
|
||||
const result: JieQiInfo[] = [];
|
||||
|
||||
try {
|
||||
// 遍历获取每个节气
|
||||
const holidays = LunarHoliday.fromYear(year);
|
||||
for (const holiday of holidays) {
|
||||
if (holiday.getName()) {
|
||||
const solarDate = holiday.getSolar();
|
||||
result.push({
|
||||
name: holiday.getName(),
|
||||
nameInChinese: holiday.getName(),
|
||||
date: new Date(solarDate.getYear(), solarDate.getMonth() - 1, solarDate.getDay()),
|
||||
});
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
// 忽略错误
|
||||
}
|
||||
|
||||
// 按日期排序
|
||||
result.sort((a, b) => 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;
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user