lunisolar
Version:
专业农历库,支持公历阴历互转,支持各类黄历数据查询,如八字四柱、阴历、神煞宜忌、时辰吉凶、建除十二神、胎神占方、五行纳音等。支持自定义插件。
196 lines (180 loc) • 6.29 kB
text/typescript
import { SB } from './stemBranch'
import { SolarTerm } from './solarTerm'
import { Lunar } from './lunar'
import {
parseDate,
computeSBMonthValueByTerm,
computeRatStem,
getDateData,
getDateOfStartOf23H
} from '../utils'
import { SB0_DATE } from '../constants/calendarData'
import { _GlobalConfig } from '../config'
export class Char8 {
readonly value: number = -1
readonly _list: [SB, SB, SB, SB]
readonly _config: Required<Char8Config> = {
changeAgeTerm: _GlobalConfig.changeAgeTerm,
isUTC: false,
lang: _GlobalConfig.lang,
offset: 0
}
constructor(dateOrSbList: Date | [SB, SB, SB, SB], config?: Char8Config) {
if (config) {
this._config = Object.assign({}, this._config, config)
}
if (dateOrSbList instanceof Date) {
const isUTC = this._config.isUTC
const date = getDateOfStartOf23H(dateOrSbList, isUTC, true)
const y = Char8.computeSBYear(date, this._config)
const m = Char8.computeSBMonth(date, this._config)
const d = Char8.computeSBDay(date, this._config)
const h = Char8.computeSBHour(date, d, this._config)
dateOrSbList = [y, m, d, h]
}
if (Array.isArray(dateOrSbList)) {
this._list = dateOrSbList
this.value = Char8.computeValue(dateOrSbList)
} else {
throw new Error('Invalid Char8')
}
}
getConfig() {
return Object.assign({}, this._config)
}
get list() {
return this._list
}
get year() {
return this._list[0]
}
get month() {
return this._list[1]
}
get day() {
return this._list[2]
}
get hour() {
return this._list[3]
}
// 日主
get me() {
return this._list[2].stem
}
static computeValue(sbList: SB[]) {
let res = 0
for (let i = 0; i < 4; i++) {
res += sbList[i].valueOf() * Math.pow(10, 2 * (3 - i))
}
return res
}
/**
* 取年的天五地支
* @param date 日期
* @returns {SB} 返回天地支对象
*/
static computeSBYear(date: Date | number, config?: Char8Config) {
let changeAgeTerm =
config && config.changeAgeTerm !== undefined
? config.changeAgeTerm
: _GlobalConfig.changeAgeTerm
const isUTC = config && config.isUTC
let year = typeof date !== 'number' ? getDateData(date, 'FullYear', isUTC) : date
if (changeAgeTerm !== null && changeAgeTerm !== undefined && typeof date !== 'number') {
// 如果 changeAgeTerm 设有值, 则按照该节气换岁
changeAgeTerm = changeAgeTerm % 24
let isPreYear = changeAgeTerm < 0
changeAgeTerm = changeAgeTerm >= 0 ? changeAgeTerm : 24 + changeAgeTerm
// 查出当前节气日期
let yearStart = getDateData(date, 'FullYear', isUTC)
if (isPreYear) yearStart--
const yearEnd = yearStart + 1
// 该年的岁的范围
const startTermDate = SolarTerm.findDate(yearStart, changeAgeTerm)
const endTermDate = SolarTerm.findDate(yearEnd, changeAgeTerm)
const startDate = parseDate(
`${startTermDate[0]}-${startTermDate[1]}-${startTermDate[2] - 1} 23:00:00`
)
const endDate = parseDate(
`${endTermDate[0]}-${endTermDate[1]}-${endTermDate[2] - 1} 23:00:00`
)
// 检查是否在该岁的范围内
if (date.valueOf() < startDate.valueOf()) year--
else if (date.valueOf() >= endDate.valueOf()) year++
} else if (changeAgeTerm === null && typeof date !== 'number') {
// changeAgeTerm 为null时,则以正月初一换岁
const theNewYearDate = Lunar.getLunarNewYearDay(year)
if (date.valueOf() < theNewYearDate.valueOf() - 60 * 60 * 1000) year--
}
const stemValue = (year - 4) % 10
const branchValue = (year - 4) % 12
return new SB(stemValue, branchValue, config)
}
/**
* 取月的天干地支
* @param date 日期
* @returns {SB} 返回天地支对象
*/
static computeSBMonth(date: Date, config?: Char8Config) {
const lang = config && config.lang !== undefined ? config.lang : _GlobalConfig.lang
let changeAgeTerm =
config && config.changeAgeTerm !== undefined
? config.changeAgeTerm
: _GlobalConfig.changeAgeTerm
changeAgeTerm = changeAgeTerm || 0
const isUTC = config?.isUTC ?? false
const findNodeConfig = {
isUTC,
lang,
returnValue: true,
nodeFlag: (changeAgeTerm + 24) % 2 // 根据changeAgeTerm的奇偶来判定是节换月还是中气换月
}
// 知道该日是哪个节气之后便可知道该日是哪个地支月
const [termValue, termDate] = SolarTerm.findNode(date, findNodeConfig) as [number, Date]
const sbValue = computeSBMonthValueByTerm(date, termValue, termDate, isUTC)
const sbConfig = {
lang
}
return new SB(sbValue, undefined, sbConfig)
}
/**
* 取日的天干地支
* @param date 日期
* @returns {SB} 返回天地支对象
*/
static computeSBDay(date: Date, config?: Char8Config) {
const isUTC = config?.isUTC || false
const offset = config?.offset || 0
const dateValue = isUTC ? date.valueOf() - offset * 60 * 1000 : date.valueOf()
// const sb0 = parseDate(`${SB0_DATE[0]}-${SB0_DATE[1]}-${SB0_DATE[2] - 1} 15:00:00`, true)
const sb0 = parseDate(`${SB0_DATE[0]}-${SB0_DATE[1]}-${SB0_DATE[2] - 1} 23:00:00`)
let daydiff = Math.floor((dateValue - sb0.valueOf()) / (1000 * 60 * 60 * 24)) % 60
if (daydiff < 0) daydiff += 60
return new SB(daydiff, undefined, config)
}
/**
* 取时辰天干地支
---- 五鼠遁 ---
甲己还加甲,乙庚丙作初。
丙辛从戊起,丁壬庚子居。
戊癸起壬子,周而复始求。
* @param date 日期
* @returns {SB} 返回天地支对象
*/
static computeSBHour(date: Date, sbDay?: SB, config?: Char8Config) {
const isUTC = config?.isUTC || false
if (!sbDay) sbDay = Char8.computeSBDay(date, config)
const hour = getDateData(date, 'Hours', isUTC)
const dayStem = sbDay.stem
// 五鼠遁方法计算子时起始天干
const branchNum = ((hour + 1) >> 1) % 12
const stemNum = computeRatStem(dayStem.value, branchNum)
return new SB(stemNum, branchNum, config)
}
toString() {
return `${this.year} ${this.month} ${this.day} ${this.hour}`
}
valueOf() {
return this.value
}
}