Browse Source

feat: add getSolarTermsInRange

Yaavi 1 year ago
parent
commit
6fd22d5907
3 changed files with 122 additions and 5 deletions
  1. 36 1
      README.md
  2. 52 3
      src/solar_terms/index.ts
  3. 34 1
      src/utils/dayjs.ts

+ 36 - 1
README.md

@@ -163,10 +163,45 @@ console.log(secondNextWorkday);
 
 ### 获取 24 节气的日期
 
+中国的二十四节气是传统的农业历法,它们标志着一年中不同的**时间段**。每个节气通常有特定的(开始)日期,但这个日期并不代表整个节气的持续时间。事实上,每个节气大约持续15天。
+
+以“小满”为例,它的日期通常在公历5月20日左右开始,但并不止于这一天,而是持续到下一个节气开始。具体来说,小满大约持续到6月5日(芒种)前后。因此,“小满”节气的时间段是从5月20日左右到6月5日左右。
+
+#### 获取范围内节气日期数组
+
+```js
+import { getSolarTermsInRange } from "chinese-days";
+
+// 不传,查询当天
+console.log(getSolarTermsInRange())
+// [{date: '2024-05-29', term: 'lesser_fullness_of_grain', name: '小满', index: 10}]
+// index: 代表处于当前节气的第几天,从 1 开始
+
+// 不传end,查询指定日期
+console.log(getSolarTermsInRange('2024-05-01'))
+// [{date: '2024-05-01', term: 'grain_rain', name: '谷雨', index: 13}]
+
+// 查询范围内的节气
+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}
+ * ]
+ **/
+```
+
+#### 如果你仅想获取节气**开始日期**数组
+
 ```js
 import { getSolarTerms } from "chinese-days";
 
-/** 获取范围内 节气日期数组 */
+/** 获取范围内 节气的开始日期数组 */
 const solarTerms = getSolarTerms("2024-05-01", "2024-05-20");
 solarTerms.forEach(({ date, term, name }) => {
   console.log(`${name}: ${date}, ${term}`);

+ 52 - 3
src/solar_terms/index.ts

@@ -44,16 +44,17 @@ export interface SolarTerm {
   date: string;
   term: SolarTermKey;
   name: string;
+  index?: number;
 };
 
 /**
- * Get solar terms => 获取范围日期内的节气
+ * Get solar terms => 获取范围日期内的节气 开始的日期
  * @param start 开始日期
  * @param end 不传只查当天
- * @returns Array of solar terms => 节气数组
+ * @returns Array of solar terms => 节气开始日数组
  */
 const getSolarTerms = (
-  start: ConfigType,
+  start?: ConfigType,
   end?: ConfigType
 ): SolarTerm[] => {
   const result: SolarTerm[] = [];
@@ -74,6 +75,7 @@ const getSolarTerms = (
           date: solarTermDate.format("YYYY-MM-DD"),
           term,
           name: SOLAR_TERMS[term],
+          index: 1,
         });
       }
     });
@@ -87,7 +89,54 @@ const getSolarTerms = (
   return result;
 };
 
+/**
+ * Get solar terms => 获取范围日期内的节气
+ * @param start 开始日期
+ * @param end 不传只查当天
+ * @returns Array of solar terms => 节气日数组
+ */
+const getSolarTermsInRange = (
+  start?: ConfigType,
+  end?: ConfigType
+): SolarTerm[] => {
+  // 从开始日减一个月 - 结束日下一个月 计算所有节气
+  let current = wrapDate(start).subtract(1, 'month');
+  const endDate = wrapDate(end || start).add(1, 'month');
+  const allTerms: { term: SolarTermKey, date: Dayjs }[] = []
+  while (current.isBefore(endDate) || current.isSame(endDate)) {
+    const year = current.year();
+    const month = current.month() + 1;
+    SOLAR_TERMS_MONTH[month].forEach((term: SolarTermKey) => {
+      const solarTermDate = dayjs(getSolarTermDate(year, month, term));
+      allTerms.push({ term, date: solarTermDate });
+    });
+    if (current.month() === 11) {
+      current = current.add(1, 'year').startOf('year');
+    } else {
+      current = current.add(1, 'month').startOf('month');
+    }
+  }
+
+  // 计算中间的所有日期
+  const deltaDays: (Omit<SolarTerm, 'date'> & {day: Dayjs})[] = []
+  allTerms.forEach((term, index) => {
+    for (let date = term.date; allTerms[index + 1] && date.isBefore(allTerms[index + 1].date); date = date.add(1, 'day')) {
+      deltaDays.push({ day: date, term: term.term, name: SOLAR_TERMS[term.term], index: date.diff(term.date, 'day') + 1 })
+    }
+  })
+
+  if (!end) end = start
+  return deltaDays.filter(trem => trem.day.isBetween(start, end, 'day')).map(trem => ({
+    date: trem.day.format('YYYY-MM-DD'),
+    term: trem.term,
+    name: trem.name,
+    index: trem.index
+  }));
+};
+
+
 export {
   getSolarTermDate,
   getSolarTerms,
+  getSolarTermsInRange,
 }

+ 34 - 1
src/utils/dayjs.ts

@@ -53,7 +53,7 @@ export class Dayjs {
     }
   }
 
-  startOf(unit: "year" | "month" | "day"): Dayjs {
+  startOf(unit?: "year" | "month" | "day"): Dayjs {
     const newDate = new Date(this._date);
     switch (unit) {
       case "year":
@@ -72,6 +72,25 @@ export class Dayjs {
     return new Dayjs(newDate);
   }
 
+  endOf(unit?: "year" | "month" | "day"): Dayjs {
+    const newDate = new Date(this._date);
+    switch (unit) {
+      case "year":
+        newDate.setMonth(11);
+        newDate.setDate(31);
+        newDate.setHours(23, 59, 59, 999);
+        break;
+      case "month":
+        newDate.setDate(new Date(newDate.getFullYear(), newDate.getMonth() + 1, 0).getDate());
+        newDate.setHours(23, 59, 59, 999);
+        break;
+      case "day":
+        newDate.setHours(23, 59, 59, 999);
+        break;
+    }
+    return new Dayjs(newDate);
+  }
+
   add(value: number, unit: "year" | "month" | "day"): Dayjs {
     const newDate = new Date(this._date);
     switch (unit) {
@@ -88,6 +107,10 @@ export class Dayjs {
     return new Dayjs(newDate);
   }
 
+  subtract(value: number, unit: "year" | "month" | "day"): Dayjs {
+    return this.add(-value, unit);
+  }
+
   format(formatStr: string): string {
     const map: { [key: string]: number | string } = {
       YYYY: this._date.getFullYear(),
@@ -177,6 +200,16 @@ export class Dayjs {
         );
     }
   }
+
+  isBetween(
+    startDate: string | number | Date | Dayjs | null | undefined,
+    endDate: string | number | Date | Dayjs | null | undefined,
+    unit?: "year" | "month" | "day"
+  ): boolean {
+    const start = new Dayjs(startDate).startOf(unit);
+    const end = new Dayjs(endDate).endOf(unit);
+    return this.isAfter(start) && this.isBefore(end) || this.isSame(start) || this.isSame(end);
+  }
 }
 
 const simpleDayjs = (date?: ConfigType): Dayjs => new Dayjs(date);