UNPKG

@marchingy/lunar

Version:

Chinese calendar with the 24 solar terms.

101 lines (100 loc) 4.23 kB
import { getPlanet } from 'ephemeris'; import { coerceInteger, toPrecision } from './tool'; import { CH_STANDARD_POSITION, SOLAR_TERMS_ZH } from './intl'; import { ChineseDate } from './lunar-calendar'; /** * @description GB/T 33661-2017 农历的编算和颁行 * @link http://c.gb688.cn/bzgk/gb/showGb?type=online&hcno=E107EA4DE9725EDF819F33C60A44B296 */ export class SolarTerm { constructor(order, label) { this.label = label; this.toString = () => { return `${this.label}` + (this.date ? ` ${this.date?.toChineseString()}` : ``); }; if (order < 1 || order > 24 || order % 1 !== 0) { throw new Error(`Illegal parameter "order": ${order}, this must be an integer from 1 to 24.`); } this.order = order; this.longitude = (order - 1) * 15; } isMidTerm() { return this.longitude % 30 === 0; } static create(index, lang = SOLAR_TERMS_ZH) { return new SolarTerm(coerceInteger(index), lang[index - 1]); } } export function create24SolarTerms(lang = SOLAR_TERMS_ZH) { const terms = new Map(); for (let i = 1; i <= 24; i++) { terms.set(i, SolarTerm.create(i, lang)); } return terms; } export function calcMoonEclipticLongitude(targetDate, coordinate = CH_STANDARD_POSITION) { return getPlanet('moon', targetDate, coordinate[0], coordinate[1], 0).observed.moon.apparentLongitudeDd; } ; export function calcSunEclipticLongitude(targetDate, coordinate = CH_STANDARD_POSITION) { return getPlanet('sun', targetDate, coordinate[0], coordinate[1], 0).observed.sun.apparentLongitudeDd; } ; export function calcDiffOfSunAndMoon(time, coordinate = CH_STANDARD_POSITION) { const sunResult = calcSunEclipticLongitude(time, coordinate); const moonResult = calcMoonEclipticLongitude(time, coordinate); return Math.min(Math.abs(sunResult - moonResult), Math.abs(sunResult - (moonResult - 360)), Math.abs((sunResult - 360) - moonResult), Math.abs((sunResult - 360) - (moonResult - 360))); } export function getTermOnDay(date, coordinate = CH_STANDARD_POSITION) { const dateS = new Date(date.getFullYear(), date.getMonth(), date.getDate()); const dateE = new Date(date.getFullYear(), date.getMonth(), date.getDate(), 23, 59, 59); let eclipticLngS = toPrecision(calcSunEclipticLongitude(dateS, coordinate), 3); const eclipticLngE = toPrecision(calcSunEclipticLongitude(dateE, coordinate), 3); let result = null; let x = Math.floor(eclipticLngE); // An integer; if (eclipticLngS > eclipticLngE) { eclipticLngS = eclipticLngS - 360; } do { if (x % 15 === 0 && eclipticLngS <= x && eclipticLngE >= x) { const index = x / 15 + 1; if (index >= 1 && index <= 24) { result = SolarTerm.create(index); result.date = new ChineseDate(date); break; } } x--; } while (x > eclipticLngS); return result; } export function countSolarTerms(fromDate, toDate, coordinate = CH_STANDARD_POSITION) { const terms = []; let startDate; let endDate; let target; if (fromDate.getTime() <= toDate.getTime()) { startDate = new Date(fromDate.getFullYear(), fromDate.getMonth(), fromDate.getDate()); endDate = new Date(toDate.getFullYear(), toDate.getMonth(), toDate.getDate()); } else { startDate = new Date(toDate.getFullYear(), toDate.getMonth(), toDate.getDate()); endDate = new Date(fromDate.getFullYear(), fromDate.getMonth(), fromDate.getDate()); } do { target = getTermOnDay(startDate, coordinate); if (target) { terms.push(target); startDate.setDate(startDate.getDate() + 13); continue; } startDate.setDate(startDate.getDate() + 1); } while (startDate.getTime() <= endDate.getTime()); return terms; } /** * This method will countSolarTerms with params that are the first date and the latest date of the year. */ export function getTermsOnYear(year, coordinate = CH_STANDARD_POSITION) { return countSolarTerms(new Date(year, 0, 1, 0, 0, 0, 0), new Date(year, 11, 31, 23, 59, 59, 999), coordinate); }