index.ts 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142
  1. import dayjs, { Dayjs, type ConfigType } from "../utils/dayjs";
  2. import { wrapDate } from '../utils'
  3. import {
  4. SOLAR_TERMS_C_NUMS,
  5. SOLAR_TERMS_DELTA,
  6. SOLAR_TERMS_MONTH,
  7. SOLAR_TERMS,
  8. type SolarTermKey,
  9. } from "./constants";
  10. /* Get solar term date => 获取节气日期 */
  11. const getSolarTermDate = (
  12. year: number,
  13. month: number,
  14. term: SolarTermKey
  15. ): string => {
  16. const century = year >= 2000 ? 21 : 20;
  17. const Y = year % 100;
  18. const D = 0.2422;
  19. const C = SOLAR_TERMS_C_NUMS[term][century === 21 ? 1 : 0];
  20. let L = Math.floor(Y / 4);
  21. if (
  22. [
  23. "lesser_cold",
  24. "greater_cold",
  25. "the_beginning_of_spring",
  26. "rain_water",
  27. ].includes(term)
  28. ) {
  29. L = Math.floor((Y - 1) / 4);
  30. }
  31. let day = Math.floor(Y * D + C) - L;
  32. const delta = SOLAR_TERMS_DELTA[`${year}_${term}` as keyof typeof SOLAR_TERMS_DELTA];
  33. if (delta) {
  34. day += delta;
  35. }
  36. return dayjs(`${year}-${month}-${day}`).format('YYYY-MM-DD');
  37. };
  38. export interface SolarTerm {
  39. date: string;
  40. term: SolarTermKey;
  41. name: string;
  42. index?: number;
  43. };
  44. /**
  45. * Get solar terms => 获取范围日期内的节气 开始的日期
  46. * @param start 开始日期
  47. * @param end 不传只查当天
  48. * @returns Array of solar terms => 节气开始日数组
  49. */
  50. const getSolarTerms = (
  51. start?: ConfigType,
  52. end?: ConfigType
  53. ): SolarTerm[] => {
  54. const result: SolarTerm[] = [];
  55. let current = wrapDate(start);
  56. const endDate = wrapDate(end || start);
  57. while (current.isBefore(endDate) || current.isSame(endDate)) {
  58. const year = current.year();
  59. const month = current.month() + 1;
  60. SOLAR_TERMS_MONTH[month].forEach((term: SolarTermKey) => {
  61. const solarTermDate = dayjs(getSolarTermDate(year, month, term));
  62. if (
  63. (solarTermDate?.isBefore(endDate) || solarTermDate?.isSame(endDate)) &&
  64. (solarTermDate?.isAfter(current) || solarTermDate?.isSame(current))
  65. ) {
  66. result.push({
  67. date: solarTermDate.format("YYYY-MM-DD"),
  68. term,
  69. name: SOLAR_TERMS[term],
  70. index: 1,
  71. });
  72. }
  73. });
  74. /* 处理下个日期 */
  75. month === 12
  76. ? (current = current.add(1, "year").startOf("year"))
  77. : (current = current.add(1, "month").startOf("month"));
  78. }
  79. return result;
  80. };
  81. /**
  82. * Get solar terms => 获取范围日期内的节气
  83. * @param start 开始日期
  84. * @param end 不传只查当天
  85. * @returns Array of solar terms => 节气日数组
  86. */
  87. const getSolarTermsInRange = (
  88. start?: ConfigType,
  89. end?: ConfigType
  90. ): SolarTerm[] => {
  91. // 从开始日减一个月 - 结束日下一个月 计算所有节气
  92. let current = wrapDate(start).subtract(1, 'month');
  93. const endDate = wrapDate(end || start).add(1, 'month');
  94. const allTerms: { term: SolarTermKey, date: Dayjs }[] = []
  95. while (current.isBefore(endDate) || current.isSame(endDate)) {
  96. const year = current.year();
  97. const month = current.month() + 1;
  98. SOLAR_TERMS_MONTH[month].forEach((term: SolarTermKey) => {
  99. const solarTermDate = dayjs(getSolarTermDate(year, month, term));
  100. allTerms.push({ term, date: solarTermDate });
  101. });
  102. if (current.month() === 11) {
  103. current = current.add(1, 'year').startOf('year');
  104. } else {
  105. current = current.add(1, 'month').startOf('month');
  106. }
  107. }
  108. // 计算中间的所有日期
  109. const deltaDays: (Omit<SolarTerm, 'date'> & {day: Dayjs})[] = []
  110. allTerms.forEach((term, index) => {
  111. for (let date = term.date; allTerms[index + 1] && date.isBefore(allTerms[index + 1].date); date = date.add(1, 'day')) {
  112. deltaDays.push({ day: date, term: term.term, name: SOLAR_TERMS[term.term], index: date.diff(term.date, 'day') + 1 })
  113. }
  114. })
  115. if (!end) end = start
  116. return deltaDays.filter(trem => trem.day.isBetween(start, end, 'day')).map(trem => ({
  117. date: trem.day.format('YYYY-MM-DD'),
  118. term: trem.term,
  119. name: trem.name,
  120. index: trem.index
  121. }));
  122. };
  123. export {
  124. getSolarTermDate,
  125. getSolarTerms,
  126. getSolarTermsInRange,
  127. }