Browse Source

v0.2.0: 完成阴历互转

Yaavi 1 year ago
parent
commit
802d69bce3

+ 65 - 10
README.en.md

@@ -85,7 +85,7 @@ console.log(getDayDetail('2024-02-04')); // { "date": "2024-02-04", "work":true,
 console.log(getDayDetail('2024-02-17')); // { "date": "2024-02-17", "work":false,"name":"Spring Festival,春节,3"}
 ```
 
-### `getHolidays` Get all holidays within a specified date range
+### `getHolidaysInRange` Get all holidays within a specified date range
 
 Receives start and end dates and optionally includes weekends. If weekends are included, the function returns all holidays including weekends; otherwise, only holidays on weekdays are returned.
 
@@ -97,15 +97,15 @@ const start = '2024-04-26';
 const end = '2024-05-06';
 
 // Get all holidays from 2024-05-01 to 2024-05-10, including weekends
-const holidaysIncludingWeekends = getHolidays(start, end, true);
+const holidaysIncludingWeekends = getHolidaysInRange(start, end, true);
 console.log('Holidays including weekends:', holidaysIncludingWeekends.map(d => getDayDetail(d)));
 
 // Get holidays from 2024-05-01 to 2024-05-10, excluding weekends
-const holidaysExcludingWeekends = getHolidays(start, end, false);
+const holidaysExcludingWeekends = getHolidaysInRange(start, end, false);
 console.log('Holidays excluding weekends:', holidaysExcludingWeekends.map(d => getDayDetail(d)));
 ```
 
-### `getWorkdays` Get a list of workdays within a specified date range
+### `getWorkdaysInRange` Get a list of workdays within a specified date range
 
 Receives start and end dates and optionally includes weekends. If weekends are included, the function returns all workdays including weekends; otherwise, only weekdays (Monday to Friday) workdays are returned.
 
@@ -115,11 +115,11 @@ const start = '2024-04-26';
 const end = '2024-05-06';
 
 // Get all workdays from 2024-05-01 to 2024-05-10, including weekends
-const workdaysIncludingWeekends = getWorkdays(start, end, true);
+const workdaysIncludingWeekends = getWorkdaysInRange(start, end, true);
 console.log('Workdays including weekends:', workdaysIncludingWeekends);
 
 // Get workdays from 2024-05-01 to 2024-05-10, excluding weekends
-const workdaysExcludingWeekends = getWorkdays(start, end, false);
+const workdaysExcludingWeekends = getWorkdaysInRange(start, end, false);
 console.log('Workdays excluding weekends:', workdaysExcludingWeekends);
 ```
 
@@ -172,12 +172,67 @@ getSolarTerms("2024-05-20");
 // return: [{date: '2024-05-20', term: 'lesser_fullness_of_grain', name: '小满'}]
 ```
 
+## Conversion Between Gregorian and Lunar Calendar
+
+Special notes for this library:
+1. `2057-09-28` is the lunar date: `丁丑` year, August 30th;
+2. `2097-08-07` is the lunar date: `丁巳` year, July 1st.
+
+### Convert Gregorian Date to Lunar Date
+
+```js
+// 2097-08-07
+console.log(getLunarDate('2097-08-07'))
+
+// 2057-09-28
+console.log(getLunarDate('2057-09-28'))
+// Output:
+// {
+//   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'))
+```
+
+### Get Lunar Dates in a Range of Gregorian Dates
+
+```js
+console.log(getLunarDatesInRange('2001-05-21', '2001-05-26'))
+```
+
+### Convert Lunar Date to Gregorian Date
+
+When dealing with a leap month in the lunar calendar, one lunar date may correspond to two different Gregorian dates, hence the return is in object form.
+
+```js
+console.log(getSolarDateFromLunar('2001-03-05'))
+// return {date: '2001-03-29', leapMonthDate: undefined}
+
+console.log(getSolarDateFromLunar('2001-04-05'))
+// return {date: '2001-04-27', leapMonthDate: '2001-05-27'}
+```
+
 ## Contributing
 
-1. Fork + Clone the project locally;
-2. Modify [holiday definitions](scripts/generate.ts);
-3. Run `npm run generate` to automatically generate [constants file](src/holidays/constants.ts);
-4. Submit a PR.
+1. Fork + Clone the project to your local machine;
+2. Holidays: Modify the [holiday definitions](scripts/generate.ts);
+3. Lunar definitions: Modify the [lunar definitions](src/solar_lunar/constants.ts);
+4. For other modifications, refer to the source code yourself;
+5. Run the command `npm run generate` to automatically generate the [holiday constants file](src/holidays/constants.ts);
+6. Submit a PR.
 
 ## Acknowledgments
 

+ 64 - 9
README.md

@@ -84,7 +84,7 @@ console.log(getDayDetail('2024-02-04')); // { "date": "2024-02-04", "work":true,
 console.log(getDayDetail('2024-02-17')); // { "date": "2024-02-17", "work":false,"name":"Spring Festival,春节,3"}
 ```
 
-### `getHolidays` 获取指定日期范围内的所有节假日
+### `getHolidaysInRange` 获取指定日期范围内的所有节假日
 
 接收起始日期和结束日期,并可选地决定是否包括周末。如果包括周末,则函数会返回包括周末在内的所有节假日;否则,只返回工作日的节假日。
 
@@ -96,16 +96,16 @@ const start = '2024-04-26';
 const end = '2024-05-06';
 
 // 获取从 2024-05-01 到 2024-05-10 的所有节假日,包括周末
-const holidaysIncludingWeekends = getHolidays(start, end, true);
+const holidaysIncludingWeekends = getHolidaysInRange(start, end, true);
 console.log('Holidays including weekends:', holidaysIncludingWeekends.map(d => getDayDetail(d)));
 
 // 获取从 2024-05-01 到 2024-05-10 的节假日,不包括周末
-const holidaysExcludingWeekends = getHolidays(start, end, false);
+const holidaysExcludingWeekends = getHolidaysInRange(start, end, false);
 console.log('Holidays excluding weekends:', holidaysExcludingWeekends.map(d => getDayDetail(d)));
 ```
 
 
-### `getWorkdays` 取指定日期范围内的工作日列表
+### `getWorkdaysInRange` 取指定日期范围内的工作日列表
 
 接收起始日期和结束日期,并可选地决定是否包括周末。如果包括周末,则函数会返回包括周末在内的所有工作日;否则,只返回周一到周五的工作日。
 
@@ -115,11 +115,11 @@ const start = '2024-04-26';
 const end = '2024-05-06';
 
 // 获取从 2024-05-01 到 2024-05-10 的所有工作日,包括周末
-const workdaysIncludingWeekends = getWorkdays(start, end, true);
+const workdaysIncludingWeekends = getWorkdaysInRange(start, end, true);
 console.log('Workdays including weekends:', workdaysIncludingWeekends);
 
 // 获取从 2024-05-01 到 2024-05-10 的工作日,不包括周末
-const workdaysExcludingWeekends = getWorkdays(start, end, false);
+const workdaysExcludingWeekends = getWorkdaysInRange(start, end, false);
 console.log('Workdays excluding weekends:', workdaysExcludingWeekends);
 ```
 
@@ -172,12 +172,67 @@ getSolarTerms("2024-05-20");
 // return: [{date: '2024-05-20', term: 'lesser_fullness_of_grain', name: '小满'}]
 ```
 
+## 阳历农历互转
+
+特别说明,此库中:
+1. `2057-09-28` 为:农历丁丑(牛)年八月三十;
+2. `2097-08-07` 为:农历丁巳(蛇)年七月初一。
+
+### 阳历转换农历
+
+```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: "牛"
+// }
+
+// 非闰月 和 闰月例子
+console.log(getLunarDate('2001-04-27'))
+console.log(getLunarDate('2001-05-27'))
+```
+
+### 根据阳历日期区间,批量获取农历日期
+
+```js
+console.log(getLunarDatesInRange('2001-05-21', '2001-05-26'))
+```
+
+### 农历转换阳历
+
+当为阴历闰月的时候,会出现一个农历日期对应两个阳历日期的情况,所以返回对象形式。
+
+```js
+console.log(getSolarDateFromLunar('2001-03-05'))
+// return {date: '2001-03-29', leapMonthDate: undefined}
+
+console.log(getSolarDateFromLunar('2001-04-05'))
+// return {date: '2001-04-27', leapMonthDate: '2001-05-27'}
+```
+
 ## 贡献代码
 
 1. Fork + Clone 项目到本地;
-2. 修改 [节假日定义](scripts/generate.ts);
-3. 执行命令 `npm run generate` 自动生成 [常量文件](src/holidays/constants.ts);
-4. 提交PR。
+2. 节假日: 修改 [节假日定义](scripts/generate.ts);
+3. 农历定义: 修改 [农历定义](src/solar_lunar/constants.ts);
+4. 其他修改需要自己查看源码;
+5. 执行命令 `npm run generate` 自动生成 [节假日常量文件](src/holidays/constants.ts);
+6. 提交PR。
 
 ## 致谢
 

+ 11 - 0
index.html

@@ -0,0 +1,11 @@
+<!DOCTYPE html>
+<html lang="en">
+  <head>
+    <meta charset="UTF-8" />
+    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
+    <title>Test</title>
+  </head>
+  <body>
+    <script type="module" src="./src/solar_lunar/index.ts"></script>
+  </body>
+</html>

+ 2 - 2
package-lock.json

@@ -1,12 +1,12 @@
 {
   "name": "china-days",
-  "version": "0.1.1",
+  "version": "0.2.0",
   "lockfileVersion": 3,
   "requires": true,
   "packages": {
     "": {
       "name": "china-days",
-      "version": "0.1.1",
+      "version": "0.2.0",
       "license": "MIT",
       "dependencies": {
         "dayjs": "^1.11.11"

+ 7 - 3
package.json

@@ -1,7 +1,7 @@
 {
   "name": "china-days",
-  "version": "0.1.1",
-  "description": "中国日期库,含有节假日、调休日、24节气,支持 TS、CommonJS、UMD 模块化使用。",
+  "version": "0.2.0",
+  "description": "中国节假日、调休日、工作日、24节气查询,农历阳历互转,支持 TS、CommonJS、UMD 模块化使用。",
   "main": "dist/index.min.js",
   "module": "dist/index.es.js",
   "types": "dist/index.d.ts",
@@ -27,8 +27,12 @@
   "keywords": [
     "中国日期",
     "节假日",
+    "工作日",
     "调休",
-    "24节气"
+    "24节气",
+    "农历",
+    "阴历",
+    "阳历"
   ],
   "author": "Yaavi",
   "license": "MIT",

+ 4 - 4
src/holidays/index.ts

@@ -67,7 +67,7 @@ const getDayDetail = (date: dayjs.ConfigType): { work: boolean, name: string, da
 }
 
 /** 获取节假日 */
-const getHolidays = (start: dayjs.ConfigType, end: dayjs.ConfigType, includeWeekends: boolean = true): string[] => {
+const getHolidaysInRange = (start: dayjs.ConfigType, end: dayjs.ConfigType, includeWeekends: boolean = true): string[] => {
   start = _validateDate(start) as Dayjs;
   end = _validateDate(end) as Dayjs;
   if (includeWeekends) {
@@ -77,7 +77,7 @@ const getHolidays = (start: dayjs.ConfigType, end: dayjs.ConfigType, includeWeek
 }
 
 /** 获取工作日 */
-const getWorkdays = (start: dayjs.ConfigType, end: dayjs.ConfigType, includeWeekends: boolean = true): string[] => {
+const getWorkdaysInRange = (start: dayjs.ConfigType, end: dayjs.ConfigType, includeWeekends: boolean = true): string[] => {
   start = _validateDate(start) as Dayjs;
   end = _validateDate(end) as Dayjs;
   if (includeWeekends) {
@@ -117,7 +117,7 @@ export {
   isWorkday,
   isInLieu,
   getDayDetail,
-  getHolidays,
-  getWorkdays,
+  getHolidaysInRange,
+  getWorkdaysInRange,
   findWorkday,
 }

+ 7 - 6
src/index.ts

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

+ 85 - 0
src/solar_lunar/constants.ts

@@ -0,0 +1,85 @@
+export interface LunarDateDetail {
+  /** 阳历日期 */
+  date: string;
+  /** 农历年份 */
+  lunarYear: number;
+  /** 农历月份 */
+  lunarMon: number;
+  /** 农历日期 */
+  lunarDay: number;
+  /** 是否闰月 */
+  isLeap: boolean;
+  /** 年柱,天干地支表示的年份 */
+  yearCyl: string;
+  /** 月柱,天干地支表示的月份 */
+  monCyl: string;
+  /** 日柱,天干地支表示的日期 */
+  dayCyl: string;
+  /** 生肖 */
+  zodiac: string;
+  /** 农历年份的中文写法 */
+  lunarYearCN: string;
+  /** 农历月份的中文写法 */
+  lunarMonCN: string;
+  /** 农历日期的中文写法 */
+  lunarDayCN: string;
+}
+
+/**
+ * LUNAR_INFO 数组值的计算原理:
+ *
+ * 每个值使用 16 进制表示,包括以下部分:
+ * 1. 前 4 位:表示闰月的月份,如果没有闰月为 0。
+ * 2. 中间 12 位:表示 1 到 12 月的大小月,1 为大月(30 天),0 为小月(29 天)。
+ * 3. 后 4 位:表示闰月的天数,如果没有闰月为 0。
+ *
+ * 以 `0x04bd8` 为例:
+ * - `0x` 表示这是一个 16 进制数。
+ * - `04bd8` 是具体的 16 进制值。
+ *
+ * 转换为二进制后,`04bd8` 为 `0000 0100 1011 1101 1000`:
+ * 1. 前 4 位 `0000`:表示该年份没有闰月(若有闰月,该值为闰月的月份)。
+ * 2. 中间 12 位 `0100 1011 1101`:从左到右分别表示 1 到 12 月的天数。`1` 表示大月(30 天),`0` 表示小月(29 天)。
+ *    - `0`(1月):小月(29 天)
+ *    - `1`(2月):大月(30 天)
+ *    - `0`(3月):小月(29 天)
+ *    - `1`(4月):大月(30 天)
+ *    - `0`(5月):小月(29 天)
+ *    - `1`(6月):大月(30 天)
+ *    - `1`(7月):大月(30 天)
+ *    - `1`(8月):大月(30 天)
+ *    - `1`(9月):大月(30 天)
+ *    - `1`(10月):大月(30 天)
+ *    - `0`(11月):小月(29 天)
+ *    - `0`(12月):小月(29 天)
+ * 3. 后 4 位 `1000`:表示闰月的天数。如果前 4 位为 `0000`(即没有闰月),则这一部分不使用。
+ */
+export const LUNAR_INFO: number[] = [
+  0x04bd8, 0x04ae0, 0x0a570, 0x054d5, 0x0d260, 0x0d950, 0x16554, 0x056a0, 0x09ad0, 0x055d2, //1900-1909
+  0x04ae0, 0x0a5b6, 0x0a4d0, 0x0d250, 0x1d255, 0x0b540, 0x0d6a0, 0x0ada2, 0x095b0, 0x14977, //1910-1919
+  0x04970, 0x0a4b0, 0x0b4b5, 0x06a50, 0x06d40, 0x1ab54, 0x02b60, 0x09570, 0x052f2, 0x04970, //1920-1929
+  0x06566, 0x0d4a0, 0x0ea50, 0x06e95, 0x05ad0, 0x02b60, 0x186e3, 0x092e0, 0x1c8d7, 0x0c950, //1930-1939
+  0x0d4a0, 0x1d8a6, 0x0b550, 0x056a0, 0x1a5b4, 0x025d0, 0x092d0, 0x0d2b2, 0x0a950, 0x0b557, //1940-1949
+  0x06ca0, 0x0b550, 0x15355, 0x04da0, 0x0a5b0, 0x14573, 0x052b0, 0x0a9a8, 0x0e950, 0x06aa0, //1950-1959
+  0x0aea6, 0x0ab50, 0x04b60, 0x0aae4, 0x0a570, 0x05260, 0x0f263, 0x0d950, 0x05b57, 0x056a0, //1960-1969
+  0x096d0, 0x04dd5, 0x04ad0, 0x0a4d0, 0x0d4d4, 0x0d250, 0x0d558, 0x0b540, 0x0b6a0, 0x195a6, //1970-1979
+  0x095b0, 0x049b0, 0x0a974, 0x0a4b0, 0x0b27a, 0x06a50, 0x06d40, 0x0af46, 0x0ab60, 0x09570, //1980-1989
+  0x04af5, 0x04970, 0x064b0, 0x074a3, 0x0ea50, 0x06b58, 0x055c0, 0x0ab60, 0x096d5, 0x092e0, //1990-1999
+  0x0c960, 0x0d954, 0x0d4a0, 0x0da50, 0x07552, 0x056a0, 0x0abb7, 0x025d0, 0x092d0, 0x0cab5, //2000-2009
+  0x0a950, 0x0b4a0, 0x0baa4, 0x0ad50, 0x055d9, 0x04ba0, 0x0a5b0, 0x15176, 0x052b0, 0x0a930, //2010-2019
+  0x07954, 0x06aa0, 0x0ad50, 0x05b52, 0x04b60, 0x0a6e6, 0x0a4e0, 0x0d260, 0x0ea65, 0x0d530, //2020-2029
+  0x05aa0, 0x076a3, 0x096d0, 0x04afb, 0x04ad0, 0x0a4d0, 0x1d0b6, 0x0d250, 0x0d520, 0x0dd45, //2030-2039
+  0x0b5a0, 0x056d0, 0x055b2, 0x049b0, 0x0a577, 0x0a4b0, 0x0aa50, 0x1b255, 0x06d20, 0x0ada0, //2040-2049
+  0x14b63, 0x09370, 0x049f8, 0x04970, 0x064b0, 0x168a6, 0x0ea50, 0x06b20, 0x1a6c4, 0x0aae0, //2050-2059
+  0x0a2e0, 0x0d2e3, 0x0c960, 0x0d557, 0x0d4a0, 0x0da50, 0x05d55, 0x056a0, 0x0a6d0, 0x055d4, //2060-2069
+  0x052d0, 0x0a9b8, 0x0a950, 0x0b4a0, 0x0b6a6, 0x0ad50, 0x055a0, 0x0aba4, 0x0a5b0, 0x052b0, //2070-2079
+  0x0b273, 0x06930, 0x07337, 0x06aa0, 0x0ad50, 0x14b55, 0x04b60, 0x0a570, 0x054e4, 0x0d160, //2080-2089
+  0x0e968, 0x0d520, 0x0daa0, 0x16aa6, 0x056d0, 0x04ae0, 0x0a9d4, 0x0a2d0, 0x0d150, 0x0f252, //2090-2099
+  0x0d520 //2100
+];
+
+export const CHINESE_NUMBER = ["零", "一", "二", "三", "四", "五", "六", "七", "八", "九"];
+export const NUMBER_MONTH: string[] = ["正", "二", "三", "四", "五", "六", "七", "八", "九", "十", "冬", "腊"];
+export const NUMBER_1: string[] = ["甲", "乙", "丙", "丁", "戊", "己", "庚", "辛", "壬", "癸"];
+export const NUMBER_2: string[] = ["子", "丑", "寅", "卯", "辰", "巳", "午", "未", "申", "酉", "戌", "亥"];
+export const ZODIACS: string[] = ["鼠", "牛", "虎", "兔", "龙", "蛇", "马", "羊", "猴", "鸡", "狗", "猪"];

+ 73 - 91
src/solar_lunar/index.ts

@@ -1,77 +1,5 @@
 import dayjs from "dayjs";
-
-interface LunarDateDetail {
-  solarDate: string;  // 阳历日期
-  lunarYear: number;  // 农历年份
-  lunarMon: number;   // 农历月份
-  lunarDay: number;   // 农历日期
-  isLeap: boolean;    // 是否闰月
-  yearCyl: string;    // 年柱,天干地支表示的年份
-  monCyl: string;     // 月柱,天干地支表示的月份
-  dayCyl: string;     // 日柱,天干地支表示的日期
-  zodiac: string;     // 生肖
-  lunarYearCN: string;  // 农历年份的中文写法
-  lunarMonCN: string;   // 农历月份的中文写法
-  lunarDayCN: string;   // 农历日期的中文写法
-}
-/**
- * LUNAR_INFO 数组值的计算原理:
- *
- * 每个值使用 16 进制表示,包括以下部分:
- * 1. 前 4 位:表示闰月的月份,如果没有闰月为 0。
- * 2. 中间 12 位:表示 1 到 12 月的大小月,1 为大月(30 天),0 为小月(29 天)。
- * 3. 后 4 位:表示闰月的天数,如果没有闰月为 0。
- *
- * 以 `0x04bd8` 为例:
- * - `0x` 表示这是一个 16 进制数。
- * - `04bd8` 是具体的 16 进制值。
- *
- * 转换为二进制后,`04bd8` 为 `0000 0100 1011 1101 1000`:
- * 1. 前 4 位 `0000`:表示该年份没有闰月(若有闰月,该值为闰月的月份)。
- * 2. 中间 12 位 `0100 1011 1101`:从左到右分别表示 1 到 12 月的天数。`1` 表示大月(30 天),`0` 表示小月(29 天)。
- *    - `0`(1月):小月(29 天)
- *    - `1`(2月):大月(30 天)
- *    - `0`(3月):小月(29 天)
- *    - `1`(4月):大月(30 天)
- *    - `0`(5月):小月(29 天)
- *    - `1`(6月):大月(30 天)
- *    - `1`(7月):大月(30 天)
- *    - `1`(8月):大月(30 天)
- *    - `1`(9月):大月(30 天)
- *    - `1`(10月):大月(30 天)
- *    - `0`(11月):小月(29 天)
- *    - `0`(12月):小月(29 天)
- * 3. 后 4 位 `1000`:表示闰月的天数。如果前 4 位为 `0000`(即没有闰月),则这一部分不使用。
- */
-const LUNAR_INFO: number[] = [
-  0x04bd8, 0x04ae0, 0x0a570, 0x054d5, 0x0d260, 0x0d950, 0x16554, 0x056a0, 0x09ad0, 0x055d2, //1900-1909
-  0x04ae0, 0x0a5b6, 0x0a4d0, 0x0d250, 0x1d255, 0x0b540, 0x0d6a0, 0x0ada2, 0x095b0, 0x14977, //1910-1919
-  0x04970, 0x0a4b0, 0x0b4b5, 0x06a50, 0x06d40, 0x1ab54, 0x02b60, 0x09570, 0x052f2, 0x04970, //1920-1929
-  0x06566, 0x0d4a0, 0x0ea50, 0x06e95, 0x05ad0, 0x02b60, 0x186e3, 0x092e0, 0x1c8d7, 0x0c950, //1930-1939
-  0x0d4a0, 0x1d8a6, 0x0b550, 0x056a0, 0x1a5b4, 0x025d0, 0x092d0, 0x0d2b2, 0x0a950, 0x0b557, //1940-1949
-  0x06ca0, 0x0b550, 0x15355, 0x04da0, 0x0a5b0, 0x14573, 0x052b0, 0x0a9a8, 0x0e950, 0x06aa0, //1950-1959
-  0x0aea6, 0x0ab50, 0x04b60, 0x0aae4, 0x0a570, 0x05260, 0x0f263, 0x0d950, 0x05b57, 0x056a0, //1960-1969
-  0x096d0, 0x04dd5, 0x04ad0, 0x0a4d0, 0x0d4d4, 0x0d250, 0x0d558, 0x0b540, 0x0b6a0, 0x195a6, //1970-1979
-  0x095b0, 0x049b0, 0x0a974, 0x0a4b0, 0x0b27a, 0x06a50, 0x06d40, 0x0af46, 0x0ab60, 0x09570, //1980-1989
-  0x04af5, 0x04970, 0x064b0, 0x074a3, 0x0ea50, 0x06b58, 0x055c0, 0x0ab60, 0x096d5, 0x092e0, //1990-1999
-  0x0c960, 0x0d954, 0x0d4a0, 0x0da50, 0x07552, 0x056a0, 0x0abb7, 0x025d0, 0x092d0, 0x0cab5, //2000-2009
-  0x0a950, 0x0b4a0, 0x0baa4, 0x0ad50, 0x055d9, 0x04ba0, 0x0a5b0, 0x15176, 0x052b0, 0x0a930, //2010-2019
-  0x07954, 0x06aa0, 0x0ad50, 0x05b52, 0x04b60, 0x0a6e6, 0x0a4e0, 0x0d260, 0x0ea65, 0x0d530, //2020-2029
-  0x05aa0, 0x076a3, 0x096d0, 0x04afb, 0x04ad0, 0x0a4d0, 0x1d0b6, 0x0d250, 0x0d520, 0x0dd45, //2030-2039
-  0x0b5a0, 0x056d0, 0x055b2, 0x049b0, 0x0a577, 0x0a4b0, 0x0aa50, 0x1b255, 0x06d20, 0x0ada0, //2040-2049
-  0x14b63, 0x09370, 0x049f8, 0x04970, 0x064b0, 0x168a6, 0x0ea50, 0x06b20, 0x1a6c4, 0x0aae0, //2050-2059
-  0x0a2e0, 0x0d2e3, 0x0c960, 0x0d557, 0x0d4a0, 0x0da50, 0x05d55, 0x056a0, 0x0a6d0, 0x055d4, //2060-2069
-  0x052d0, 0x0a9b8, 0x0a950, 0x0b4a0, 0x0b6a6, 0x0ad50, 0x055a0, 0x0aba4, 0x0a5b0, 0x052b0, //2070-2079
-  0x0b273, 0x06930, 0x07337, 0x06aa0, 0x0ad50, 0x14b55, 0x04b60, 0x0a570, 0x054e4, 0x0d160, //2080-2089
-  0x0e968, 0x0d520, 0x0daa0, 0x16aa6, 0x056d0, 0x04ae0, 0x0a9d4, 0x0a2d0, 0x0d150, 0x0f252, //2090-2099
-  0x0d520 //2100
-];
-
-const CHINESE_NUMBER = ["零", "一", "二", "三", "四", "五", "六", "七", "八", "九"];
-const NUMBER_MONTH: string[] = ["正", "二", "三", "四", "五", "六", "七", "八", "九", "十", "冬", "腊"];
-const NUMBER_1: string[] = ["甲", "乙", "丙", "丁", "戊", "己", "庚", "辛", "壬", "癸"];
-const NUMBER_2: string[] = ["子", "丑", "寅", "卯", "辰", "巳", "午", "未", "申", "酉", "戌", "亥"];
-const ZODIACS: string[] = ["鼠", "牛", "虎", "兔", "龙", "蛇", "马", "羊", "猴", "鸡", "狗", "猪"];
+import { type LunarDateDetail, LUNAR_INFO, CHINESE_NUMBER, NUMBER_MONTH, NUMBER_1, NUMBER_2, ZODIACS } from './constants'
 
 /**
  * 获取指定农历年的天数
@@ -156,7 +84,7 @@ const getLunarYearText = (lunarYear: number): string => {
  * @param endYear 结束农历年份
  * @returns 农历年份列表
  */
-const getYears = (startYear: number, endYear: number) => {
+const getLunarYears = (startYear: number, endYear: number) => {
   const years = [];
   for (let i = startYear; i <= endYear; i++) {
     years.push({
@@ -169,11 +97,11 @@ const getYears = (startYear: number, endYear: number) => {
 }
 
 /**
- * 获取指定年份的所有农历月份
+ * 获取指定阳历年份的闰月
  * @param year 年份
  * @returns 农历闰月月份
  */
-export const getYearLeapMonth = (year: number) => {
+const getYearLeapMonth = (year: number) => {
   const leap = yearLeapMonth(year)
   return {
     year,
@@ -250,7 +178,7 @@ export const getLunarDate = (date: dayjs.ConfigType): LunarDateDetail => {
   lunarDate[2] = offset + 1; // 农历日期
 
   return {
-    solarDate: objDate.format('YYYY-MM-DD'), // 公历日期
+    date: objDate.format('YYYY-MM-DD'), // 公历日期
     lunarYear: lunarDate[0], // 农历年份
     lunarMon: lunarDate[1] + 1, // 农历月份
     lunarDay: lunarDate[2], // 农历日期
@@ -265,22 +193,76 @@ export const getLunarDate = (date: dayjs.ConfigType): LunarDateDetail => {
   };
 }
 
-console.log(Date.now())
-// 2057-9-28
-console.log(getLunarDate('2057-09-28'))
-// 农历丁丑(牛)年八月三十
+/**
+ * 获取范围内所有日期的农历信息
+ * @param startDate 开始日期
+ * @param endDate 结束日期
+ * @returns 范围内所有日期的农历信息
+ */
+export const getLunarDatesInRange = (startDate: dayjs.ConfigType, endDate: dayjs.ConfigType): LunarDateDetail[] => {
+  const start = dayjs(startDate);
+  const end = dayjs(endDate);
+  const lunarDates: LunarDateDetail[] = [];
+
+  for (let date = start; date.isBefore(end) || date.isSame(end, 'day'); date = date.add(1, 'day')) {
+    lunarDates.push(getLunarDate(date));
+  }
+
+  return lunarDates;
+}
+
+/**
+ * 根据阴历日期查询阳历日期
+ * @param lunarDate 农历日期
+ * @param isLeapMonth 是否闰月
+ * @returns 阳历日期
+ */
+export const getSolarDateFromLunar = (lunarDate: dayjs.ConfigType): {
+  date: string;
+  leapMonthDate?: string;
+} => {
+  const date = dayjs(lunarDate);
+  const lunarYear = date.year();
+  const lunarMonth = date.month() + 1;
+  const lunarDay = date.date();
+
+  // 计算从农历年开始到指定农历日期的总天数
+  let offset = 0;
+  for (let i = 1900; i < lunarYear; i++) {
+    offset += lunarYearDays(i);
+  }
+
+  let leapMonth = yearLeapMonth(lunarYear);
+  for (let i = 1; i < lunarMonth; i++) {
+    offset += monthDays(lunarYear, i);
+    if (i === leapMonth) {
+      offset += yearLeapDays(lunarYear);
+    }
+  }
+
+  offset += lunarDay - 1;
 
-// 2097-8-7
-console.log(getLunarDate('2097-08-07'))
-// 农历丁巳(蛇)年七月一
+  // 通过在基准日期上添加偏移量来获取阳历日期
+  const baseDate = dayjs(new Date(1900, 0, 31));
+  const solarDate = baseDate.add(offset, 'day').format('YYYY-MM-DD');
 
-// 非闰月
-console.log(getLunarDate('2001-04-27'))
-// 闰月
-console.log(getLunarDate('2001-05-27'))
 
-console.log(getYearLeapMonth(2001))
+  /* 闰月日期 */
+  let leapMonthDateOffset = offset;
+  let solarLeapMonthDate: string | undefined;
+  if (leapMonth === lunarMonth) {
+    leapMonthDateOffset += monthDays(lunarYear, lunarMonth);
+    solarLeapMonthDate = baseDate.add(leapMonthDateOffset, 'day').format('YYYY-MM-DD');
+  }
 
+  return {
+    date: solarDate,
+    leapMonthDate: solarLeapMonthDate,
+  };
+}
 
-console.log(getYears(2000, 2003))
-console.log(Date.now())
+export default {
+  getLunarDate,
+  getLunarDatesInRange,
+  getSolarDateFromLunar,
+}

+ 7 - 8
test/holidays/index.test.ts

@@ -1,5 +1,4 @@
 import dayjs from 'dayjs';
-import { wrapDate, getDates } from '../../src/utils';
 import { holidays, workdays, inLieuDays } from '../../src/holidays/constants';
 import Arrangement, { Holiday } from '../../src/holidays/arrangement';
 import {
@@ -7,10 +6,10 @@ import {
   isWorkday,
   isInLieu,
   getDayDetail,
-  getHolidays,
-  getWorkdays,
+  getHolidaysInRange,
+  getWorkdaysInRange,
   findWorkday,
-} from '../../src/holidays';
+} from '../../src';
 
 describe('Holiday Functions', () => {
   test('isHoliday should return correct boolean values', () => {
@@ -48,18 +47,18 @@ describe('Holiday Functions', () => {
     });
   });
 
-  test('getHolidays should return correct holidays within a range', () => {
+  test('getHolidaysInRange should return correct holidays within a range', () => {
     const start = '2024-05-01';
     const end = '2024-05-31';
-    const holidaysInRange = getHolidays(start, end, true);
+    const holidaysInRange = getHolidaysInRange(start, end, true);
 
     expect(holidaysInRange).toContain('2024-05-01');
   });
 
-  test('getWorkdays should return correct workdays within a range', () => {
+  test('getWorkdaysInRange should return correct workdays within a range', () => {
     const start = '2024-05-01';
     const end = '2024-05-31';
-    const workdaysInRange = getWorkdays(start, end, true);
+    const workdaysInRange = getWorkdaysInRange(start, end, true);
 
     expect(workdaysInRange).toContain('2024-05-06');
   });

+ 145 - 0
test/solar_lunar/index.test.ts

@@ -0,0 +1,145 @@
+import {
+  getLunarDate,
+  getLunarDatesInRange,
+  getSolarDateFromLunar,
+} from "../../src";
+
+describe("solar_lunar", () => {
+  test("getLunarDate should return correct lunar date for a given solar date", () => {
+    let result = getLunarDate("2057-09-28");
+    expect(result).toEqual({
+      date: "2057-09-28",
+      lunarYear: 2057,
+      lunarMon: 8,
+      lunarDay: 30,
+      isLeap: false,
+      lunarDayCN: "三十",
+      lunarMonCN: "八月",
+      lunarYearCN: "二零五七",
+      yearCyl: "丁丑",
+      monCyl: "己酉",
+      dayCyl: "戊子",
+      zodiac: "牛",
+    });
+
+    result = getLunarDate("2097-08-07");
+    expect(result).toEqual({
+      date: "2097-08-07",
+      dayCyl: "丙寅",
+      isLeap: false,
+      lunarDay: 1,
+      lunarDayCN: "初一",
+      lunarMon: 7,
+      lunarMonCN: "七月",
+      lunarYear: 2097,
+      lunarYearCN: "二零九七",
+      monCyl: "戊申",
+      yearCyl: "丁巳",
+      zodiac: "蛇",
+    });
+
+    result = getLunarDate("2001-04-27");
+    expect(result.isLeap).toBe(false);
+
+    result = getLunarDate("2001-05-27");
+    expect(result.isLeap).toBe(true);
+  });
+
+  test("getSolarDateFromLunar should return correct solar date for a given lunar date", () => {
+    let result = getSolarDateFromLunar("2001-03-05");
+    expect(result).toEqual({ date: "2001-03-29", leapMonthDate: undefined });
+
+    result = getSolarDateFromLunar("2001-04-05");
+    expect(result).toEqual({ date: "2001-04-27", leapMonthDate: "2001-05-27" });
+  });
+
+  test("getLunarDatesInRange should return correct lunar dates for a given solar date range", () => {
+    let result = getLunarDatesInRange("2001-05-21", "2001-05-26");
+    expect(result).toEqual([
+      {
+        date: "2001-05-21",
+        lunarYear: 2001,
+        lunarMon: 4,
+        lunarDay: 29,
+        isLeap: false,
+        zodiac: "蛇",
+        yearCyl: "辛巳",
+        monCyl: "癸巳",
+        dayCyl: "甲申",
+        lunarYearCN: "二零零一",
+        lunarMonCN: "四月",
+        lunarDayCN: "廿九",
+      },
+      {
+        date: "2001-05-22",
+        lunarYear: 2001,
+        lunarMon: 4,
+        lunarDay: 30,
+        isLeap: false,
+        zodiac: "蛇",
+        yearCyl: "辛巳",
+        monCyl: "癸巳",
+        dayCyl: "乙酉",
+        lunarYearCN: "二零零一",
+        lunarMonCN: "四月",
+        lunarDayCN: "三十",
+      },
+      {
+        date: "2001-05-23",
+        lunarYear: 2001,
+        lunarMon: 5,
+        lunarDay: 1,
+        isLeap: false,
+        zodiac: "蛇",
+        yearCyl: "辛巳",
+        monCyl: "甲午",
+        dayCyl: "丙戌",
+        lunarYearCN: "二零零一",
+        lunarMonCN: "五月",
+        lunarDayCN: "初一",
+      },
+      {
+        date: "2001-05-24",
+        lunarYear: 2001,
+        lunarMon: 4,
+        lunarDay: 2,
+        isLeap: true,
+        zodiac: "蛇",
+        yearCyl: "辛巳",
+        monCyl: "癸巳",
+        dayCyl: "丁亥",
+        lunarYearCN: "二零零一",
+        lunarMonCN: "四月",
+        lunarDayCN: "初二",
+      },
+      {
+        date: "2001-05-25",
+        lunarYear: 2001,
+        lunarMon: 4,
+        lunarDay: 3,
+        isLeap: true,
+        zodiac: "蛇",
+        yearCyl: "辛巳",
+        monCyl: "癸巳",
+        dayCyl: "戊子",
+        lunarYearCN: "二零零一",
+        lunarMonCN: "四月",
+        lunarDayCN: "初三",
+      },
+      {
+        date: "2001-05-26",
+        lunarYear: 2001,
+        lunarMon: 4,
+        lunarDay: 4,
+        isLeap: true,
+        zodiac: "蛇",
+        yearCyl: "辛巳",
+        monCyl: "癸巳",
+        dayCyl: "己丑",
+        lunarYearCN: "二零零一",
+        lunarMonCN: "四月",
+        lunarDayCN: "初四",
+      },
+    ]);
+  });
+});

+ 1 - 1
test/solar_terms/index.test.ts

@@ -3,7 +3,7 @@ import {
   getSolarTermDate,
   getSolarTerms,
   type SolarTerm,
-} from "../../src/solar_terms";
+} from "../../src";
 
 import type { SolarTermKey } from "../../src/solar_terms/constants";