UNPKG

lunisolar

Version:

专业农历库,支持公历阴历互转,支持各类黄历数据查询,如八字四柱、阴历、神煞宜忌、时辰吉凶、建除十二神、胎神占方、五行纳音等。支持自定义插件。

157 lines (146 loc) 5.56 kB
import { FIRST_YEAR, TERM_MINIMUM_DATES, TERM_SAME_HEX, TERM_LIST } from '../constants/lunarData' import { _GlobalConfig } from '../config' import { parseDate, getDateData } from '../utils' export class SolarTerm { readonly value: number = -1 readonly _config: Required<ClassCommonConfig> = { lang: _GlobalConfig.lang, isUTC: false } constructor(value: number | string | SolarTerm, config?: ClassCommonConfig) { if (config) { this._config = Object.assign({}, this._config, config) } if (value instanceof SolarTerm) return value if (typeof value === 'number') { this.value = value % 24 } else if (typeof value === 'string') { const termIndex = _GlobalConfig.locales[this._config.lang].solarTerm.indexOf(value) if (termIndex === -1) throw new Error('Invalid term value') this.value = termIndex } } get name() { return String(_GlobalConfig.locales[this._config.lang].solarTerm[this.value]) } static getNames(lang?: string): string[] { lang = lang || _GlobalConfig.lang return [..._GlobalConfig.locales[lang].solarTerm] } /** * 取得当年的节气日期列表 [d, d, d, d...] * @param year 年份 * @returns {number[]} */ static getYearTermDayList(year: number): number[] { const binData = TERM_SAME_HEX[TERM_LIST[year - FIRST_YEAR]].toString(2) const res = [] let temp = binData.padStart(48, '0') while (res.length < 24) { const currDate = parseInt(temp.slice(temp.length - 2), 2) const minDate: number = TERM_MINIMUM_DATES[res.length] res.push(currDate + minDate) temp = temp.slice(0, temp.length - 2) } return res } /** * 取得某年某月的两个节气的日期 * @param year 年 * @param month 月 * @returns {[number, number]} [节, 气] */ static getMonthTerms(year: number, month: number): [number, number] { // js 位运算最大只支持32位,所以要先转成字符串截取 const data = TERM_SAME_HEX[TERM_LIST[year - FIRST_YEAR]].toString(2).padStart(48, '0') const cutLen = (month - 1) * 4 const monthTermData = parseInt(data.slice(data.length - cutLen - 4, data.length - cutLen), 2) const term1 = (monthTermData & 3) + TERM_MINIMUM_DATES[(month - 1) * 2] const term2 = ((monthTermData >> 2) & 3) + TERM_MINIMUM_DATES[(month - 1) * 2 + 1] return [term1, term2] } // 查出指定节气的日期 [year, month, day] static findDate( year: number, termValue: number | string | SolarTerm, config?: ClassCommonConfig ): [number, number, number] { const lang = config && config.lang ? config.lang : _GlobalConfig.lang if (termValue instanceof SolarTerm) termValue = termValue.value termValue = typeof termValue === 'string' ? _GlobalConfig.locales[lang].solarTerm.indexOf(termValue) : termValue % 24 const month = termValue >> 1 const dayList = SolarTerm.getYearTermDayList(year) const day = dayList[termValue] return [year, month + 1, day] } /** * 查出指定日期属于哪个节或气之后,并返回该节气及该节气日期 * @param date 日期 * @param config 设置 - nodeFlag: number - lang: string - isUTC: boolean * @returns {[Term | number, number]} [节气, 节气日期] */ static findNode<T extends boolean = false>( date: Date, config: TermFindNodeConfig<T> ): [T extends true ? number : SolarTerm, Date] static findNode(date: Date, config?: TermFindNodeConfig<boolean>): [SolarTerm | number, Date] { const configDefault: TermFindNodeConfig0 = { lang: _GlobalConfig.lang, returnValue: false, nodeFlag: 0, isUTC: false } const cfg = config ? Object.assign({}, configDefault, config) : configDefault const { returnValue, nodeFlag } = cfg if (nodeFlag > 2) throw new Error('Invalid nodeFlag') const newSolarTermConfig = { lang: cfg.lang || _GlobalConfig.lang } let year = getDateData(date, 'FullYear', cfg.isUTC) let month = getDateData(date, 'Month', cfg.isUTC) const d = getDateData(date, 'Date', cfg.isUTC) const h = getDateData(date, 'Hours', cfg.isUTC) let termValue = (month * 2 + 24) % 24 // 取得该月的节的value值 let [termDay1, termDay2] = SolarTerm.getMonthTerms(year, month + 1) let usePreMonth = false let beforeTerm2 = false if (d < termDay1 && !(d === termDay1 - 1 && h >= 23)) { // 当日期在节前, 则取上一个月 usePreMonth = true } else if (d < termDay2 && !(d === termDay2 - 1 && h >= 23)) { beforeTerm2 = true // 当日期在气之前节之后,前且nodeFlag要求返回气,则取上一个月 if (nodeFlag === 1) usePreMonth = true } let termDay: number let returnTerm2 = false if (usePreMonth) { if (month - 1 < 0) { year-- month = 11 } else { month-- } termValue = (month * 2 + 24) % 24 ;[termDay1, termDay2] = SolarTerm.getMonthTerms(year, month + 1) if (nodeFlag > 0) returnTerm2 = true } else if (nodeFlag === 1 || (nodeFlag === 2 && !beforeTerm2)) returnTerm2 = true termDay = returnTerm2 ? termDay2 : termDay1 termValue = returnTerm2 ? (termValue + 1) % 24 : termValue const termDate = parseDate(`${year}-${month + 1}-${termDay}`) if (returnValue) return [termValue, termDate] return [new SolarTerm(termValue, newSolarTermConfig), termDate] } valueOf() { return this.value } toString() { return this.name } }