Browse Source

Merge pull request #26 from vsme/main

merge main
Yawei sun 2 months ago
parent
commit
1c87a1ebc4

+ 4 - 0
CHANGELOG.md

@@ -1,5 +1,9 @@
 # CHANGELOG
 # CHANGELOG
 
 
+## [1.5.0](https://github.com/vsme/chinese-days) (2025-01-24)
+
+- 增加中国农历民俗节日与纪念日
+
 ## [1.4.0](https://github.com/vsme/chinese-days) (2024-11-21)
 ## [1.4.0](https://github.com/vsme/chinese-days) (2024-11-21)
 
 
 - 完善法定节假日的放假天数,比如 `2007-05-01` 法定节假日为 `Labour Day,劳动节,3`,`2024-05-01` 法定节假日为 `Labour Day,劳动节,1`,而 `2025-05-01` 为 `Labour Day,劳动节,2`,最后一个数字 `3`、`1`、`2` 对应每年法定节假日的精准放假天数。
 - 完善法定节假日的放假天数,比如 `2007-05-01` 法定节假日为 `Labour Day,劳动节,3`,`2024-05-01` 法定节假日为 `Labour Day,劳动节,1`,而 `2025-05-01` 为 `Labour Day,劳动节,2`,最后一个数字 `3`、`1`、`2` 对应每年法定节假日的精准放假天数。

+ 3 - 0
docs/.vitepress/config.ts

@@ -16,6 +16,9 @@ export default defineConfig({
 
 
   themeConfig: {
   themeConfig: {
     logo: { src: '/icon.png', width: 24, height: 24 },
     logo: { src: '/icon.png', width: 24, height: 24 },
+    search: {
+      provider: 'local',
+    }
   },
   },
 
 
   locales: {
   locales: {

+ 3 - 1
docs/.vitepress/en.ts

@@ -41,6 +41,7 @@ function sidebarGuide(): DefaultTheme.SidebarItem[] {
       collapsed: false,
       collapsed: false,
       items: [
       items: [
         { text: 'What is Chinese Days', link: 'what-is-chinese-days' },
         { text: 'What is Chinese Days', link: 'what-is-chinese-days' },
+        { text: 'iCal Subscription', link: 'ical-subscription' },
         { text: 'Getting started', link: 'getting-started' },
         { text: 'Getting started', link: 'getting-started' },
       ]
       ]
     },
     },
@@ -65,7 +66,8 @@ function sidebarGuide(): DefaultTheme.SidebarItem[] {
       collapsed: false,
       collapsed: false,
       items: [
       items: [
         { text: 'Solar to Lunar', link: 'to-lunar' },
         { text: 'Solar to Lunar', link: 'to-lunar' },
-        { text: 'Lunar to Solar', link: 'from-lunar' }
+        { text: 'Lunar to Solar', link: 'from-lunar' },
+        { text: 'Lunar Folk Festival', link: 'lunar-folk-festival' },
       ]
       ]
     },
     },
     { text: 'Else',
     { text: 'Else',

+ 5 - 3
docs/.vitepress/zh.ts

@@ -64,7 +64,8 @@ function sidebarGuide(): DefaultTheme.SidebarItem[] {
       text: '简介',
       text: '简介',
       collapsed: false,
       collapsed: false,
       items: [
       items: [
-        { text: 'Chinese Days', link: 'what-is-chinese-days' },
+        { text: '关于本项目', link: 'what-is-chinese-days' },
+        { text: '日历订阅', link: 'ical-subscription' },
         { text: '快速开始', link: 'getting-started' },
         { text: '快速开始', link: 'getting-started' },
       ]
       ]
     },
     },
@@ -85,11 +86,12 @@ function sidebarGuide(): DefaultTheme.SidebarItem[] {
       ]
       ]
     },
     },
     {
     {
-      text: '阴历/农历',
+      text: '农历(阴历)',
       collapsed: false,
       collapsed: false,
       items: [
       items: [
         { text: '阳历转农历', link: 'to-lunar' },
         { text: '阳历转农历', link: 'to-lunar' },
-        { text: '农历转阳历', link: 'from-lunar' }
+        { text: '农历转阳历', link: 'from-lunar' },
+        { text: '民俗节日与纪念日', link: 'lunar-folk-festival' },
       ]
       ]
     },
     },
     { text: '其他',
     { text: '其他',

+ 63 - 0
docs/en/guide/24-solar-terms.md

@@ -0,0 +1,63 @@
+# Solar Terms Module
+
+China's 24 solar terms are a part of the traditional agricultural calendar, marking distinct **periods** within a year. Each solar term typically begins on a specific (start) date but represents a period lasting about 15 days until the next solar term starts.
+
+For example, the solar term **"Lesser Fullness of Grain" (小满)** begins around **May 20th** in the Gregorian calendar. However, it doesn't end on that day; instead, it lasts until the next solar term, **"Grain in Ear" (芒种)**, which starts around **June 5th**. Therefore, the "Lesser Fullness of Grain" period is roughly from May 20th to June 5th.
+
+---
+
+## Retrieve Dates of the 24 Solar Terms
+
+### Get Solar Term Dates Within a Range
+
+```js
+import { getSolarTermsInRange } from "chinese-days";
+
+// If no arguments are passed, it returns the solar term for the current day
+console.log(getSolarTermsInRange());
+// [{date: '2024-05-29', term: 'lesser_fullness_of_grain', name: '小满', index: 10}]
+// index: Represents the day count within the current solar term, starting from 1
+
+// Query a specific date if only the start date is provided
+console.log(getSolarTermsInRange('2024-05-01'));
+// [{date: '2024-05-01', term: 'grain_rain', name: '谷雨', index: 13}]
+
+// Query solar terms within a date range
+console.log(getSolarTermsInRange('2024-05-01', '2024-05-06'));
+/**
+ * =>
+ * [
+ *   {"date":"2024-05-01","term":"grain_rain","name":"谷雨","index":13},
+ *   {"date":"2024-05-02","term":"grain_rain","name":"谷雨","index":14},
+ *   {"date":"2024-05-03","term":"grain_rain","name":"谷雨","index":15},
+ *   {"date":"2024-05-04","term":"grain_rain","name":"谷雨","index":16},
+ *   {"date":"2024-05-05","term":"the_beginning_of_summer","name":"立夏","index":1},
+ *   {"date":"2024-05-06","term":"the_beginning_of_summer","name":"立夏","index":2}
+ * ]
+ */
+```
+
+---
+
+### Retrieve Only the Start Dates of Solar Terms
+
+```js
+import { getSolarTerms } from "chinese-days";
+
+/** Get the array of solar term start dates within a specified range */
+const solarTerms = getSolarTerms("2024-05-01", "2024-05-20");
+solarTerms.forEach(({ date, term, name }) => {
+  console.log(`${name}: ${date}, ${term}`);
+});
+// Output:
+// 立夏: 2024-05-05, the_beginning_of_summer
+// 小满: 2024-05-20, lesser_fullness_of_grain
+
+// If there are no solar terms within the range, return []
+console.log(getSolarTerms("2024-05-21", "2024-05-25"));
+// Output: []
+
+// Query the solar term for a specific day if no end date is provided
+console.log(getSolarTerms("2024-05-20"));
+// Output: [{date: '2024-05-20', term: 'lesser_fullness_of_grain', name: '小满'}]
+```

+ 7 - 0
docs/en/guide/contributing.md

@@ -0,0 +1,7 @@
+# Contributing Code
+
+1. **Fork + Clone** the repository to your local environment.
+2. **Holidays**: Update the [holiday definitions](https://github.com/vsme/chinese-days/blob/main/src/holidays/generate.ts).
+3. **Lunar Calendar**: Update the [lunar calendar definitions](https://github.com/vsme/chinese-days/blob/main/src/solar_lunar/constants.ts).
+4. **Other Changes**: Implement any other necessary updates or enhancements.
+5. **Submit a Pull Request (PR)**.

+ 11 - 0
docs/en/guide/from-lunar.md

@@ -0,0 +1,11 @@
+# Lunar to Gregorian Calendar Conversion
+
+When dealing with leap months in the Lunar Calendar, a single Lunar date may correspond to two Gregorian dates. In such cases, the function returns an object to handle both dates.
+
+```js
+console.log(getSolarDateFromLunar('2001-03-05'));
+// {date: '2001-03-29', leapMonthDate: undefined}
+
+console.log(getSolarDateFromLunar('2001-04-05'));
+// {date: '2001-04-27', leapMonthDate: '2001-05-27'}
+```

+ 68 - 0
docs/en/guide/getting-started.md

@@ -0,0 +1,68 @@
+# Quick Start
+
+This documentation is primarily intended for users developing in `JS` and `TS`.
+
+::: info Note
+For other programming languages, please refer to the section on [Non-JS Languages](/en/guide/what-is-chinese-days.html#non-js-languages).
+
+Additionally, the project supports `iCal` file [holiday subscriptions](/en/guide/ical-subscription), compatible with clients such as Google Calendar, Apple Calendar, and Microsoft Outlook.
+:::
+
+## Recommended Usage
+
+Directly include the library in the browser for the most up-to-date version:
+
+```html
+<!-- Include the script -->
+<script src="https://cdn.jsdelivr.net/npm/chinese-days"></script>
+
+<!-- Use the library -->
+<script>
+  chineseDays.isHoliday('2024-01-01');
+  // Or use destructuring
+  const { isHoliday } = chineseDays;
+</script>
+```
+
+Alternatively, use `type="module"` with ESM:
+
+```html
+<script type="module">
+  import chineseDays from 'https://esm.run/chinese-days';
+  // Use the library after importing
+  chineseDays.isHoliday('2024-01-01');
+</script>
+```
+
+## Installation
+
+```sh
+npm i chinese-days
+```
+
+Using ESM import:
+
+```ts
+import chineseDays from 'chinese-days';
+console.log(chineseDays);
+```
+
+Using in Node.js:
+
+```js
+const { isWorkday, isHoliday } = require('chinese-days');
+console.log(isWorkday('2020-01-01'));
+console.log(isHoliday('2020-01-01'));
+```
+
+## Specifying a Version
+
+It is generally not recommended to specify a version, as holidays are updated based on announcements from the State Council. If you must specify a version, you can refer to [jsdelivr](https://www.jsdelivr.com/), for example:
+
+```
+https://cdn.jsdelivr.net/npm/chinese-days@1.4.0
+```
+
+```
+https://esm.run/chinese-days@1.4.0
+```

+ 35 - 0
docs/en/guide/holidays.md

@@ -0,0 +1,35 @@
+# Holidays
+
+## `isHoliday` Check if a given date is a holiday
+
+```js
+console.log(isHoliday('2023-01-01')); // true
+```
+
+## `getHolidaysInRange` Retrieve all holidays within a specified date range
+
+This function accepts a start date, an end date, and an optional parameter to decide whether to include weekends. If weekends are included, the function returns all holidays, including those that fall on weekends. If weekends are excluded, it only returns holidays on workdays.
+
+::: info Note
+Even if weekends are excluded, holidays that fall on weekends will still be included in the result.
+:::
+
+```js
+// Example usage
+const start = '2024-04-26';
+const end = '2024-05-06';
+
+// Get all holidays from 2024-04-26 to 2024-05-06, including weekends
+const holidaysIncludingWeekends = getHolidaysInRange(start, end, true);
+console.log(
+  'Holidays including weekends:',
+  holidaysIncludingWeekends.map((d) => getDayDetail(d))
+);
+
+// Get holidays from 2024-04-26 to 2024-05-06, excluding weekends
+const holidaysExcludingWeekends = getHolidaysInRange(start, end, false);
+console.log(
+  'Holidays excluding weekends:',
+  holidaysExcludingWeekends.map((d) => getDayDetail(d))
+);
+```

+ 15 - 0
docs/en/guide/ical-subscription.md

@@ -0,0 +1,15 @@
+# Calendar Subscription
+
+You can set up a subscription URL in clients such as Google Calendar, Apple Calendar, or Microsoft Outlook to access holiday calendars.
+
+The subscribed calendar includes holidays and adjusted working days for the upcoming three years (2023–2025).
+
+### English Subscription URL: 
+```
+https://cdn.jsdelivr.net/npm/chinese-days/dist/holidays.en.ics
+```
+
+### Chinese Subscription URL:
+```
+https://cdn.jsdelivr.net/npm/chinese-days/dist/holidays.ics
+```

+ 17 - 0
docs/en/guide/lieu-days.md

@@ -0,0 +1,17 @@
+# In Lieu Days
+
+## `isInLieu` Check if a given date is an in lieu day
+
+In the context of China's holiday arrangements, **in lieu days** (调休日) are adjustments to regular workdays or weekends to create longer continuous holidays or make up for days off. For example, a weekend may be shifted to a workday, or a workday may become a rest day, to facilitate an extended holiday period.
+
+### Example Usage
+
+```js
+// Check if May 2, 2024, is an in lieu day. 
+// If `true`, it indicates the date is an in lieu day.
+console.log(isInLieu('2024-05-02')); // true
+
+// Check if May 1, 2024, is an in lieu day.
+// If `false`, it indicates the date is not an in lieu day.
+console.log(isInLieu('2024-05-01')); // false
+```

+ 94 - 0
docs/en/guide/lunar-folk-festival.md

@@ -0,0 +1,94 @@
+# Lunar Folk Festival
+
+```js
+/**
+ * Retrieve lunar festivals (includes fixed festivals and specially calculated festivals)
+ * @param [start] Start date (default: current day)
+ * @param [end] End date (when omitted, only queries the start date)
+ */
+getLunarFestivals('2025-01-28');
+getLunarFestivals('2025-01-28', '2025-01-30');
+```
+
+### 农历民俗节日与纪念日
+
+#### 正月
+- **初一**: 春节、鸡日、神话中元始天尊诞辰
+- **初二**: 犬日
+- **初三**: 猪日、小年朝
+- **初四**: 羊日、孙勵天医(孙思邈)诞辰
+- **初五**: 牛日、破五日、开市、神话中路神诞辰
+- **初六**: 马日
+- **初七**: 人日、送火神
+- **初八**: 谷日、神话中阎王诞辰
+- **初九**: 天日、神话中玉皇诞辰
+- **初十**: 地日、石头生日(祭石头)
+- **十三**: 上(试)灯日、关公升天日
+- **十五**: 元宵节、正灯日、上元节、神话中天官诞辰
+- **十八**: 落灯日
+- **二十五**: 天仓(填仓)节(日)
+
+#### 二月
+- **初一**: 太阳生日
+- **初二**: 神话中济公活佛生日、春龙节(龙抬头)、神话中土地公生日
+- **初三**: 神话中文昌帝君诞辰
+- **十二**: 百花生日(花朝)
+- **十五**: 神话中九天玄女娘娘诞辰、神话中太上老君诞辰、精忠岳王诞辰
+- **十九**: 神话中观音菩萨诞辰
+- **清明前一天**: 寒食节
+- **廿一**: 神话中普贤菩萨诞辰
+
+#### 三月
+- **初三**: 上巳、新媳妇回娘家
+- **十五**: 神话中赵公元帅诞辰、神话中泰山老母诞辰
+
+#### 四月
+- **初一**: 祭雹神
+- **初四**: 神话中文殊菩萨诞辰
+- **初八**: 浴佛节(龙华会)
+- **十二**: 神话中蛇王诞辰
+- **十四**: 神话中神仙生日(吕洞宾诞辰)
+- **十八**: 神医华佗诞辰
+- **廿八**: 药王诞辰(神农诞辰)
+
+#### 五月
+- **初五**: 端午节
+- **十三**: 雨节(黄帝诞辰)
+
+#### 六月
+- **初一**: 半年节(过半年)
+- **初六**: 晒衣节、祭山神
+- **十九**: 神话中观音菩萨得道
+- **廿四**: 关公诞辰、神话中雷神诞辰、荷花生日
+
+#### 七月
+- **初一**: 祭海神
+- **初七**: 女儿节(乞巧节)
+- **十五**: 中元节(鬼节)、神话中地官诞辰(孟兰盆会)
+- **十八**: 神话中西王母诞辰
+- **二十**: 棉花生日
+- **廿三**: 诸葛武侯诞辰
+- **三十**: 神话中地藏菩萨诞辰
+
+#### 八月
+- **初一**: 天医节
+- **初三**: 神话中灶君生日
+- **初八**: 神话中瑶池大会
+- **十五**: 中秋节
+- **二十**: 水稻生日
+- **廿八**: 孔子诞辰
+
+#### 九月
+- **初九**: 重阳节
+- **十九**: 神话中观音菩萨出家
+
+#### 十月
+- **初一**: 十月朝、寒衣节(散工日)
+- **十五**: 下元节、神话中水官诞辰
+
+#### 十二月
+- **初八**: 腊八节
+- **廿三**: 送灶日(官)
+- **廿四**: 送灶日(民)
+- **廿五**: 神话中接玉皇
+- **除夕**: 封井、祭井神、贴春联、迎财神

+ 4 - 0
docs/en/guide/thank.md

@@ -0,0 +1,4 @@
+# Acknowledgments
+
+1. Lunar calendar data is sourced from the [Bigkoo/Android-PickerView](https://github.com/Bigkoo/Android-PickerView) project.
+2. Chinese holiday data generation was inspired by the `Python` version of the [LKI/chinese-calendar](https://github.com/LKI/chinese-calendar) project.

+ 44 - 0
docs/en/guide/to-lunar.md

@@ -0,0 +1,44 @@
+# Gregorian to Lunar Calendar Conversion
+
+::: info Tip
+The lunar calendar, also known as the Chinese calendar, is referred to as the "Lunar Calendar" in this project.
+:::
+
+::: info Special Notes
+1. `2057-09-28` corresponds to the Lunar Calendar date: Ding Chou (Ox) Year, Eighth Month, Thirtieth Day;
+2. `2097-08-07` corresponds to the Lunar Calendar date: Ding Si (Snake) Year, Seventh Month, First Day.
+:::
+
+## Convert Gregorian Date to Lunar Date
+
+```js
+// 2097-8-7
+console.log(getLunarDate('2097-08-07'));
+
+// 2057-9-28
+console.log(getLunarDate('2057-09-28'));
+// {
+//   date: "2057-09-28",
+//   lunarYear: 2057,
+//   lunarMon: 8,
+//   lunarDay: 30,
+//   isLeap: false,
+//   lunarDayCN: "三十",
+//   lunarMonCN: "八月",
+//   lunarYearCN: "二零五七",
+//   yearCyl: "丁丑",
+//   monCyl: "己酉",
+//   dayCyl: "戊子",
+//   zodiac: "牛"
+// }
+
+// Examples of non-leap and leap months
+console.log(getLunarDate('2001-04-27'));
+console.log(getLunarDate('2001-05-27'));
+```
+
+## Batch Retrieve Lunar Dates for a Range of Gregorian Dates
+
+```js
+console.log(getLunarDatesInRange('2001-05-21', '2001-05-26'));
+```

+ 27 - 0
docs/en/guide/what-is-chinese-days.md

@@ -0,0 +1,27 @@
+# What is Chinese Days
+
+## Introduction
+
+This project provides a collection of functions for querying Chinese holidays, in lieu days, working days, the 24 solar terms, and converting between the lunar and solar calendars. Additionally, it supports subscribing to holiday calendars in `iCal` format, which can be integrated with clients such as Google Calendar, Apple Calendar, and Microsoft Outlook.
+
+Data is automatically fetched daily through `GitHub Actions`. Notifications are sent via email when there are changes to holiday schedules, and information is updated based on announcements from the State Council.
+
++ **Holidays**: Covers the years from 2004 to 2025, including the extended Spring Festival in 2020.
++ **24 Solar Terms**: Supports dates from 1900 to 2100.
++ **Lunar Calendar**: Supports dates from 1900 to 2100.
+
+The project primarily targets `JS` and `TS` users. Refer to the documentation for usage instructions.
+
+## Non-`JS` Languages
+
+If you are not developing projects in `JS` or `TS`, this project provides a [chinese-days.json](https://cdn.jsdelivr.net/npm/chinese-days/dist/chinese-days.json) file for Chinese holidays. You can use the following CDN link:
+
+```
+https://cdn.jsdelivr.net/npm/chinese-days/dist/chinese-days.json
+```
+
+For example, in `Java`, you can refer to [Warnier-zhang/java-chinese-days](https://github.com/Warnier-zhang/java-chinese-days), which is designed solely for querying Chinese holidays, adjusted working days, and regular workdays.
+
+## License Information
+
+Released under the MIT License.

+ 77 - 0
docs/en/guide/working-days.md

@@ -0,0 +1,77 @@
+# Working Days
+
+## `isWorkday` Check if a given date is a workday
+
+```js
+console.log(isWorkday('2023-01-01')); // false
+```
+
+## `getWorkdaysInRange` Get a list of workdays within a specified date range
+
+This function takes a start date, an end date, and an optional parameter to decide whether to include weekends. If weekends are included, the function returns all days within the range. Otherwise, it returns only weekdays (Monday to Friday).
+
+```js
+// Example usage
+const start = '2024-04-26';
+const end = '2024-05-06';
+
+// Get all workdays from 2024-04-26 to 2024-05-06, including weekends
+const workdaysIncludingWeekends = getWorkdaysInRange(start, end, true);
+console.log('Workdays including weekends:', workdaysIncludingWeekends);
+
+// Get workdays from 2024-04-26 to 2024-05-06, excluding weekends
+const workdaysExcludingWeekends = getWorkdaysInRange(start, end, false);
+console.log('Workdays excluding weekends:', workdaysExcludingWeekends);
+```
+
+## `findWorkday` Find a workday
+
+Find the `{deltaDays}`-th workday starting from today.
+
+```js
+// Find the {deltaDays}-th workday from today
+// If deltaDays is 0, first check if today is a workday. If yes, return today's date.
+// If today is not a workday, find the next workday.
+const currentWorkday = findWorkday(0);
+console.log(currentWorkday);
+
+// Find the next workday
+const nextWorkday = findWorkday(1);
+console.log(nextWorkday);
+
+// Find the previous workday
+const previousWorkday = findWorkday(-1);
+console.log(previousWorkday);
+
+// Specify a starting date to find workdays relative to it
+// Find the second workday from 2024-05-18
+const secondNextWorkday = findWorkday(2, '2024-05-18');
+console.log(secondNextWorkday);
+```
+
+## `getDayDetail` Get detailed date information
+
+This function checks if a specified date is a workday and returns a boolean indicating whether it is a workday, along with details about the date.
+
+1. If the specified date is a workday, it returns `true` and the name of the weekday. If it is a rescheduled workday (due to holiday adjustments), it returns `true` and details about the holiday.
+2. If it is a holiday, it returns `false` and holiday details.
+
+```js
+// Example usage
+
+// A regular workday (Friday)
+console.log(getDayDetail('2024-02-02')); 
+// { "date": "2024-02-02", "work": true, "name": "Friday" }
+
+// A holiday (weekend)
+console.log(getDayDetail('2024-02-03')); 
+// { "date": "2024-02-03", "work": false, "name": "Saturday" }
+
+// A rescheduled workday
+console.log(getDayDetail('2024-02-04')); 
+// { "date": "2024-02-04", "work": true, "name": "Spring Festival, 春节, 3" }
+
+// A Spring Festival holiday
+console.log(getDayDetail('2024-02-17')); 
+// { "date": "2024-02-17", "work": false, "name": "Spring Festival, 春节, 3" }
+```

+ 12 - 1
docs/zh/guide/getting-started.md

@@ -1,6 +1,14 @@
 # 快速开始
 # 快速开始
 
 
-本文档内容主要针对 `JS` 和 `TS` 开发用户,使用其他语言请参考 [开发](/guide/what-is-chinese-days#非-js-语言)。
+本文档内容主要针对 `JS` 和 `TS` 开发用户。
+
+::: info 提示
+使用其他语言开发,请参考 [非 JS/TS 语言](/guide/what-is-chinese-days#非-js-语言) 部分内容。
+:::
+
+::: info 订阅
+此外还支持 `iCal` 文件 [订阅节假日](/guide/ical-subscription),可供 Google Calendar、Apple Calendar、Microsoft Outlook 等客户端订阅。
+:::
 
 
 ## 推荐方式
 ## 推荐方式
 
 
@@ -55,5 +63,8 @@ console.log(isHoliday('2020-01-01'));
 
 
 ```
 ```
 https://cdn.jsdelivr.net/npm/chinese-days@1.4.0
 https://cdn.jsdelivr.net/npm/chinese-days@1.4.0
+```
+
+```
 https://esm.run/chinese-days@1.4.0
 https://esm.run/chinese-days@1.4.0
 ```
 ```

+ 3 - 1
docs/zh/guide/holidays.md

@@ -10,7 +10,9 @@ console.log(isHoliday('2023-01-01')); // true
 
 
 接收起始日期和结束日期,并可选地决定是否包括周末。如果包括周末,则函数会返回包括周末在内的所有节假日;否则,只返回工作日的节假日。
 接收起始日期和结束日期,并可选地决定是否包括周末。如果包括周末,则函数会返回包括周末在内的所有节假日;否则,只返回工作日的节假日。
 
 
-> tip: 即使不包括周末,周末的节假日仍然会被返回
+::: info 提示
+即使不包括周末,周末的节假日仍然会被返回
+:::
 
 
 ```js
 ```js
 // 示例用法
 // 示例用法

+ 15 - 0
docs/zh/guide/ical-subscription.md

@@ -0,0 +1,15 @@
+# 日历订阅
+
+在 Google Calendar、Apple Calendar、Microsoft Outlook 等客户端中,可以设置订阅地址来获取日历订阅。
+
+订阅的日历包含近三年(2023-2025年)的节假日和调休日。
+
+中文订阅地址:
+```
+https://cdn.jsdelivr.net/npm/chinese-days/dist/holidays.ics
+```
+
+英文订阅地址: 
+```
+https://cdn.jsdelivr.net/npm/chinese-days/dist/holidays.en.ics
+```

+ 96 - 0
docs/zh/guide/lunar-folk-festival.md

@@ -0,0 +1,96 @@
+# 农历民俗节日与纪念日
+
+## 根据阳历日期区间,批量农历民俗节日与纪念日
+
+```js
+/**
+ * 获取农历节日(包含固定节日和特殊计算节日)
+ * @param [start] 开始日期(默认当天)
+ * @param [end] 结束日期(不传时仅查询当天)
+ */
+getLunarFestivals('2025-01-28');
+getLunarFestivals('2025-01-28', '2025-01-30');
+```
+
+### 农历民俗节日与纪念日
+
+#### 正月
+- **初一**: 春节、鸡日、神话中元始天尊诞辰
+- **初二**: 犬日
+- **初三**: 猪日、小年朝
+- **初四**: 羊日、孙勵天医(孙思邈)诞辰
+- **初五**: 牛日、破五日、开市、神话中路神诞辰
+- **初六**: 马日
+- **初七**: 人日、送火神
+- **初八**: 谷日、神话中阎王诞辰
+- **初九**: 天日、神话中玉皇诞辰
+- **初十**: 地日、石头生日(祭石头)
+- **十三**: 上(试)灯日、关公升天日
+- **十五**: 元宵节、正灯日、上元节、神话中天官诞辰
+- **十八**: 落灯日
+- **二十五**: 天仓(填仓)节(日)
+
+#### 二月
+- **初一**: 太阳生日
+- **初二**: 神话中济公活佛生日、春龙节(龙抬头)、神话中土地公生日
+- **初三**: 神话中文昌帝君诞辰
+- **十二**: 百花生日(花朝)
+- **十五**: 神话中九天玄女娘娘诞辰、神话中太上老君诞辰、精忠岳王诞辰
+- **十九**: 神话中观音菩萨诞辰
+- **清明前一天**: 寒食节
+- **廿一**: 神话中普贤菩萨诞辰
+
+#### 三月
+- **初三**: 上巳、新媳妇回娘家
+- **十五**: 神话中赵公元帅诞辰、神话中泰山老母诞辰
+
+#### 四月
+- **初一**: 祭雹神
+- **初四**: 神话中文殊菩萨诞辰
+- **初八**: 浴佛节(龙华会)
+- **十二**: 神话中蛇王诞辰
+- **十四**: 神话中神仙生日(吕洞宾诞辰)
+- **十八**: 神医华佗诞辰
+- **廿八**: 药王诞辰(神农诞辰)
+
+#### 五月
+- **初五**: 端午节
+- **十三**: 雨节(黄帝诞辰)
+
+#### 六月
+- **初一**: 半年节(过半年)
+- **初六**: 晒衣节、祭山神
+- **十九**: 神话中观音菩萨得道
+- **廿四**: 关公诞辰、神话中雷神诞辰、荷花生日
+
+#### 七月
+- **初一**: 祭海神
+- **初七**: 女儿节(乞巧节)
+- **十五**: 中元节(鬼节)、神话中地官诞辰(孟兰盆会)
+- **十八**: 神话中西王母诞辰
+- **二十**: 棉花生日
+- **廿三**: 诸葛武侯诞辰
+- **三十**: 神话中地藏菩萨诞辰
+
+#### 八月
+- **初一**: 天医节
+- **初三**: 神话中灶君生日
+- **初八**: 神话中瑶池大会
+- **十五**: 中秋节
+- **二十**: 水稻生日
+- **廿八**: 孔子诞辰
+
+#### 九月
+- **初九**: 重阳节
+- **十九**: 神话中观音菩萨出家
+
+#### 十月
+- **初一**: 十月朝、寒衣节(散工日)
+- **十五**: 下元节、神话中水官诞辰
+
+#### 十二月
+- **初八**: 腊八节
+- **廿三**: 送灶日(官)
+- **廿四**: 送灶日(民)
+- **廿五**: 神话中接玉皇
+- **除夕**: 封井、祭井神、贴春联、迎财神

+ 6 - 1
docs/zh/guide/to-lunar.md

@@ -1,8 +1,13 @@
 # 阳历农历互转
 # 阳历农历互转
 
 
-特别说明,此库中:
+::: info 小提示
+农历又称阴历,本项目中统一称为农历。
+:::
+
+::: info 特别说明
 1. `2057-09-28` 为:农历丁丑(牛)年八月三十;
 1. `2057-09-28` 为:农历丁丑(牛)年八月三十;
 2. `2097-08-07` 为:农历丁巳(蛇)年七月初一。
 2. `2097-08-07` 为:农历丁巳(蛇)年七月初一。
+:::
 
 
 ## 阳历转换农历
 ## 阳历转换农历
 
 

+ 6 - 10
docs/zh/guide/what-is-chinese-days.md

@@ -2,7 +2,7 @@
 
 
 ## 介绍
 ## 介绍
 
 
-本项目提供了一系列用于查询中国节假日、调休日、工作日、24节气、以及农历阳历互转的函数,此外还支持 `iCal` 文件订阅节假日,可供 Google Calendar、Apple Calendar、Microsoft Outlook 等客户端订阅。
+本项目提供了一系列用于查询中国节假日、调休日、工作日、24节气、以及农历阳历互转的函数,此外还支持 `iCal` 文件订阅节假日,可供 Google Calendar、Apple Calendar、Microsoft Outlook 等客户端订阅。
 
 
 每日会定时执行 `Github Action` 自动抓取数据,节假日变化时发送邮件提醒,信息会跟随国务院发布进行更新。
 每日会定时执行 `Github Action` 自动抓取数据,节假日变化时发送邮件提醒,信息会跟随国务院发布进行更新。
 
 
@@ -14,17 +14,13 @@
 
 
 ## 非 `JS` 语言
 ## 非 `JS` 语言
 
 
-如果你不使用 `JS` 或 `TS` 开发项目,本项目提供了中国节假日的 `JSON` 文件,通过链接 [chinese-days.json](https://cdn.jsdelivr.net/npm/chinese-days/dist/chinese-days.json) 可以直接引用。
+如果你不使用 `JS` 或 `TS` 开发项目,本项目提供了中国节假日的 [chinese-days.json](https://cdn.jsdelivr.net/npm/chinese-days/dist/chinese-days.json) 文件,可以通过下面的 CDN 链接进行使用。
 
 
-比如在 `Java` 中使用,可以参考 [Warnier-zhang/java-chinese-days](https://github.com/Warnier-zhang/java-chinese-days),仅用于查询中国节假日、调休日、工作日;
+```
+https://cdn.jsdelivr.net/npm/chinese-days/dist/chinese-days.json
+```
 
 
-## 日历订阅
-
-在 Google Calendar、Apple Calendar、Microsoft Outlook 等客户端中,可以设置订阅地址:[https://cdn.jsdelivr.net/npm/chinese-days/dist/holidays.ics](https://cdn.jsdelivr.net/npm/chinese-days/dist/holidays.ics) 来获取日历订阅。
-
-For English: [https://cdn.jsdelivr.net/npm/chinese-days/dist/holidays.en.ics](https://cdn.jsdelivr.net/npm/chinese-days/dist/holidays.en.ics)
-
-订阅的日历包含近三年(2023-2025年)的节假日和调休日。
+比如在 `Java` 中使用,可以参考 [Warnier-zhang/java-chinese-days](https://github.com/Warnier-zhang/java-chinese-days),仅用于查询中国节假日、调休日、工作日。
 
 
 ## 许可信息
 ## 许可信息
 
 

File diff suppressed because it is too large
+ 0 - 0
docs/zh/index.md


+ 8 - 8
package-lock.json

@@ -1,12 +1,12 @@
 {
 {
   "name": "chinese-days",
   "name": "chinese-days",
-  "version": "1.4.0",
+  "version": "1.5.0",
   "lockfileVersion": 3,
   "lockfileVersion": 3,
   "requires": true,
   "requires": true,
   "packages": {
   "packages": {
     "": {
     "": {
       "name": "chinese-days",
       "name": "chinese-days",
-      "version": "1.4.0",
+      "version": "1.5.0",
       "license": "MIT",
       "license": "MIT",
       "devDependencies": {
       "devDependencies": {
         "@types/jest": "^29.5.12",
         "@types/jest": "^29.5.12",
@@ -2418,9 +2418,9 @@
       "dev": true
       "dev": true
     },
     },
     "node_modules/axios": {
     "node_modules/axios": {
-      "version": "1.7.5",
-      "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.5.tgz",
-      "integrity": "sha512-fZu86yCo+svH3uqJ/yTdQ0QHpQu5oL+/QE+QPSv6BZSkDAoky9vytxp7u5qk83OJFS3kEBcesWni9WTZAv3tSw==",
+      "version": "1.8.2",
+      "resolved": "https://registry.npmjs.org/axios/-/axios-1.8.2.tgz",
+      "integrity": "sha512-ls4GYBm5aig9vWx8AWDSGLpnpDQRtWAfrjU+EuytuODrFBkqesN2RkOQCBzrA1RQNHw1SmRMSDDDSwzNAYQ6Rg==",
       "dev": true,
       "dev": true,
       "license": "MIT",
       "license": "MIT",
       "dependencies": {
       "dependencies": {
@@ -5667,9 +5667,9 @@
       "license": "MIT"
       "license": "MIT"
     },
     },
     "node_modules/undici": {
     "node_modules/undici": {
-      "version": "6.19.8",
-      "resolved": "https://registry.npmjs.org/undici/-/undici-6.19.8.tgz",
-      "integrity": "sha512-U8uCCl2x9TK3WANvmBavymRzxbfFYG+tAu+fgx3zxQy3qdagQqBLwJVrdyO1TBfUXvfKveMKJZhpvUYoOjM+4g==",
+      "version": "6.21.2",
+      "resolved": "https://registry.npmjs.org/undici/-/undici-6.21.2.tgz",
+      "integrity": "sha512-uROZWze0R0itiAKVPsYhFov9LxrPMHLMEQFszeI2gCN6bnIIZ8twzBCJcN2LJrBBLfrP0t1FW0g+JmKVl8Vk1g==",
       "dev": true,
       "dev": true,
       "license": "MIT",
       "license": "MIT",
       "engines": {
       "engines": {

+ 1 - 1
package.json

@@ -1,6 +1,6 @@
 {
 {
   "name": "chinese-days",
   "name": "chinese-days",
-  "version": "1.4.0",
+  "version": "1.5.0",
   "description": "中国节假日、调休日、工作日、24节气查询,农历阳历互转,支持 TS、CommonJS、UMD 模块化使用,提供 ics 日历格式,可供 Google Calendar、Apple Calendar、Microsoft Outlook 等客户端订阅。",
   "description": "中国节假日、调休日、工作日、24节气查询,农历阳历互转,支持 TS、CommonJS、UMD 模块化使用,提供 ics 日历格式,可供 Google Calendar、Apple Calendar、Microsoft Outlook 等客户端订阅。",
   "main": "dist/index.min.js",
   "main": "dist/index.min.js",
   "module": "dist/index.es.js",
   "module": "dist/index.es.js",

+ 3 - 0
src/index.ts

@@ -1,15 +1,18 @@
 import * as HolidayUtils from "./holidays";
 import * as HolidayUtils from "./holidays";
 import * as SolarTerm from "./solar_terms";
 import * as SolarTerm from "./solar_terms";
 import * as SolarLunar from "./solar_lunar";
 import * as SolarLunar from "./solar_lunar";
+import * as LunarFolkFestival from "./lunar_folk_festival";
 
 
 // 单独导出这些方法和类型
 // 单独导出这些方法和类型
 export * from "./holidays";
 export * from "./holidays";
 export * from "./solar_terms";
 export * from "./solar_terms";
 export * from "./solar_lunar";
 export * from "./solar_lunar";
+export * from "./lunar_folk_festival";
 
 
 // 默认导出所有
 // 默认导出所有
 export default {
 export default {
   ...HolidayUtils,
   ...HolidayUtils,
   ...SolarTerm,
   ...SolarTerm,
   ...SolarLunar,
   ...SolarLunar,
+  ...LunarFolkFestival,
 }
 }

+ 124 - 0
src/lunar_folk_festival/constants.ts

@@ -0,0 +1,124 @@
+import { getLunarDate, monthDays } from "../solar_lunar";
+import { getSolarTermsInRange } from "../solar_terms";
+import { type Dayjs } from "../utils/dayjs";
+
+// 特殊节日处理器类型
+export interface LunarFestival {
+  date: string;     // 节日日期(公历)
+  name: string;     // 节日名称
+  type: 'lunar' | 'solar_term' | 'special';
+}
+
+// 固定农历节日配置
+export const LUNAR_FESTIVAL_MAP: Record<number, Record<number, string[]>> = {
+  1: { // 正月
+    1: ['春节', '鸡日', '元始天尊诞辰'],
+    2: ['犬日'],
+    3: ['猪日', '小年朝'],
+    4: ['羊日', '孙天医诞辰'],
+    5: ['牛日', '破五日', '开市', '路神诞辰'],
+    6: ['马日'],
+    7: ['人日', '送火神'],
+    8: ['谷日', '阎王诞辰'],
+    9: ['天日', '玉皇诞辰'],
+    10: ['地日', '石头生日'],
+    13: ['上(试)灯日', '关公升天日'],
+    15: ['元宵节', '上元节', '正灯日', '天官诞辰'],
+    18: ['落灯日'],
+    25: ['天仓(填仓)节'],
+  },
+  2: { // 二月
+    1: ['太阳生日'],
+    2: ['春龙节', '土地公生日', '济公活佛生日'],
+    3: ['文昌帝君诞辰'],
+    12: ['百花生日(花朝节)'],
+    15: ['九天玄女诞辰', '太上老君诞辰', '精忠岳王诞辰'],
+    19: ['观音菩萨诞辰'],
+    21: ['普贤菩萨诞辰'],
+  },
+  3: {
+    3: ['上巳节'],
+    15: ['赵公元帅诞辰', '泰山老母诞辰'],
+  },
+  4: {
+    1: ['祭雹神'],
+    4: ['文殊菩萨诞辰'],
+    8: ['浴佛节(龙华会)'],
+    12: ['蛇王诞辰'],
+    14: ['吕洞宾诞辰'],
+    18: ['华佗诞辰'],
+    28: ['药王(神农)诞辰'],
+  },
+  5: {
+    5: ['端午节'],
+    13: ['雨节', '黄帝诞辰'],
+  },
+  6: {
+    1: ['半年节'],
+    6: ['晒衣节'],
+    19: ['观音菩萨得道'],
+    24: ['雷神诞辰', '荷花生日', '关公诞辰'],
+  },
+  7: {
+    1: ['祭海神'],
+    7: ['乞巧节'],
+    15: ['中元(鬼)节', '地官诞辰(孟兰盆会)'],
+    18: ['西王母诞辰'],
+    20: ['棉花生日'],
+    23: ['诸葛亮诞辰'],
+    30: ['地藏菩萨诞辰'],
+  },
+  8: {
+    1: ['天医节'],
+    3: ['灶君生日'],
+    8: ['瑶池大会'],
+    15: ['中秋节'],
+    20: ['水稻生日'],
+    28: ['孔子诞辰'],
+  },
+  9: {
+    9: ['重阳节'],
+    19: ['观音菩萨出家'],
+  },
+  10: {
+    1: ['十月朝', '寒衣节'],
+    15: ['下元节', '水官诞辰'],
+  },
+  12: {
+    8: ['腊八节'],
+    23: ['官家送灶'],
+    24: ['民间送灶'],
+    25: ['接玉皇'],
+  }
+};
+
+// 特殊节日处理器
+export const SPECIAL_FESTIVAL_HANDLERS: ((date: Dayjs, result: LunarFestival[]) => void)[] = [
+  // 处理寒食节(清明前一日)
+  (current, result) => {
+    const pureBrightnessDay = current.add(1, 'day')
+    const pureBrightness = getSolarTermsInRange(pureBrightnessDay).find(t => t.term === 'pure_brightness');
+    if (pureBrightness) {
+      result.push({
+        date: current.format('YYYY-MM-DD'),
+        name: '寒食节',
+        type: 'solar_term'
+      });
+    }
+  },
+
+  // 处理除夕(农历腊月最后一日)
+  (current, result) => {
+    const lunar = getLunarDate(current);
+    if (lunar.lunarMon === 12 && lunar.lunarDay === monthDays(lunar.lunarYear, 12)) {
+      const date = current.format('YYYY-MM-DD');
+      ['除夕', '封井', '祭井神', '贴春联', '迎财神'].forEach(name => {
+        result.push({
+          date,
+          name,
+          type: 'lunar'
+        });
+      })
+    }
+  }
+];

+ 54 - 0
src/lunar_folk_festival/index.ts

@@ -0,0 +1,54 @@
+import { type ConfigType } from "../utils/dayjs";
+import dayjs from '../utils/dayjs';
+import { getLunarDate } from "../solar_lunar";
+import { LUNAR_FESTIVAL_MAP, SPECIAL_FESTIVAL_HANDLERS, type LunarFestival } from './constants'
+
+/**
+ * 获取农历节日(包含固定节日和特殊计算节日)
+ * @param start 开始日期
+ * @param end 结束日期
+ */
+export const getLunarFestivals = (
+  start?: ConfigType,
+  end?: ConfigType
+): { date: string, name: string[] }[] => {
+  const results: LunarFestival[] = [];
+  let current = dayjs(start);
+  const endDate = dayjs(end || start);
+
+  // 遍历日期范围
+  while (current.isBefore(endDate) || current.isSame(endDate)) {
+    // 处理固定农历节日
+    const lunar = getLunarDate(current);
+    if (!lunar.isLeap) {
+      const festivals = LUNAR_FESTIVAL_MAP[lunar.lunarMon]?.[lunar.lunarDay] || [];
+      festivals.forEach(name => {
+        results.push({
+          date: current.format('YYYY-MM-DD'),
+          name,
+          type: 'lunar'
+        });
+      });
+    }
+
+    // 运行特殊节日处理器
+    SPECIAL_FESTIVAL_HANDLERS.forEach(handler => handler(current, results));
+
+    current = current.add(1, 'day');
+  }
+
+  // 去重并排序
+  return results.reduce((acc: { date: string; name: string[] }[], curr) => {
+    const existing = acc.find(item => item.date === curr.date)
+    if (existing) {
+      existing.name.push(curr.name)
+    } else {
+      acc.push({ date: curr.date, name: [curr.name] })
+    }
+    return acc
+  }, [])
+};
+
+export default {
+  getLunarFestivals
+}

+ 1 - 1
src/solar_lunar/index.ts

@@ -41,7 +41,7 @@ const cyclicalm = (num: number): string => NUMBER_1[num % 10] + NUMBER_2[num % 1
  * @param m 农历月份
  * @param m 农历月份
  * @returns 月份天数
  * @returns 月份天数
  */
  */
-const monthDays = (y: number, m: number): number => (LUNAR_INFO[y - 1900] & (0x10000 >> m)) === 0 ? 29 : 30;
+export const monthDays = (y: number, m: number): number => (LUNAR_INFO[y - 1900] & (0x10000 >> m)) === 0 ? 29 : 30;
 
 
 /**
 /**
  * 获取指定年份的生肖
  * 获取指定年份的生肖

+ 84 - 0
test/lunar_folk_festival/index.test.ts

@@ -0,0 +1,84 @@
+import { getLunarFestivals } from "../../src";
+
+describe("lunarFestivals", () => {
+  test("getLunarFestivals should return fixed lunar festivals", () => {
+    // 测试常规固定节日
+    const result = getLunarFestivals("2025-01-29");
+    expect(result).toEqual([
+      {
+        date: "2025-01-29",
+        name: ["春节", "鸡日", "元始天尊诞辰"],
+      }
+    ]);
+  });
+
+  test("should handle solar term related festivals", () => {
+    // 测试寒食节(清明前一日)
+    const result = getLunarFestivals("2025-04-03");
+    expect(result).toEqual([{
+      date: "2025-04-03",
+      name: ["寒食节"],
+    }]);
+  });
+
+  test("should handle special festivals", () => {
+    // 测试除夕(农历腊月最后一日)
+    const result = getLunarFestivals("2025-01-28");
+    expect(result).toEqual([{
+      date: "2025-01-28",
+      name: ["除夕", "封井", "祭井神", "贴春联", "迎财神"],
+    }]);
+  });
+
+  test("should filter leap month festivals", () => {
+    const result1 = getLunarFestivals("2025-06-30")
+    expect(result1).toEqual([{
+      date: "2025-06-30",
+      name: ["晒衣节"],
+    }]);
+
+    // 测试闰月不返回节日
+    const result2 = getLunarFestivals("2025-07-30")
+    expect(result2).toEqual([]);
+  });
+
+  test("should handle cross-year scenarios", () => {
+    // 测试多天与跨年场景
+    const result = getLunarFestivals("2024-11-15", "2025-01-30");
+
+    expect(result).toEqual([
+      {
+        date: "2024-11-15",
+        name: ["下元节", "水官诞辰"],
+      },
+      {
+        date: "2025-01-07",
+        name: ["腊八节"],
+      },
+      {
+        date: "2025-01-22",
+        name: ["官家送灶"],
+      },
+      {
+        date: "2025-01-23",
+        name: ["民间送灶"],
+      },
+      {
+        date: "2025-01-24",
+        name: ["接玉皇"],
+      },
+      {
+        date: "2025-01-28",
+        name: ["除夕", "封井", "祭井神", "贴春联", "迎财神"],
+      },
+      {
+        date: "2025-01-29",
+        name: ["春节", "鸡日", "元始天尊诞辰"],
+      },
+      {
+        date: "2025-01-30",
+        name: ["犬日"],
+      },
+    ]);
+  });
+});

+ 1 - 0
test/utils/dayjs.test.ts

@@ -21,6 +21,7 @@ describe("SimpleDayjs", () => {
     expect(date1.diff(date2, "year")).toBe(24);
     expect(date1.diff(date2, "year")).toBe(24);
     expect(date1.diff(date2, "month")).toBe(289);
     expect(date1.diff(date2, "month")).toBe(289);
     expect(date1.diff(date2, "day")).toBe(8806);
     expect(date1.diff(date2, "day")).toBe(8806);
+    expect(date1.diff(date2)).toBe(8806);
   });
   });
 
 
   it("should format the start of the year", () => {
   it("should format the start of the year", () => {

Some files were not shown because too many files changed in this diff