package calendar import ( "fmt" "math" "sync" "time" "b612.me/astro/basic" ) const ( hanQingJieQiMinYear = -104 hanQingJieQiMaxYear = 1912 hanQingJieQiPatternCount = 97 hanQingJieQiPatternLength = 23 ) func hanQingCalendricalJieQiDate(year, term int) (time.Time, error) { termIndex, err := calendricalJieQiTableTermIndex(term) if err != nil { return time.Time{}, err } if year < hanQingJieQiMinYear || year > hanQingJieQiMaxYear { return time.Time{}, fmt.Errorf("该年份暂不支持历法节气") } return hanQingCalendricalJieQiDateInRow(year, termIndex) } func hanQingCalendricalJieQiDateInRow(rowYear, termIndex int) (time.Time, error) { yearIndex := rowYear - hanQingJieQiMinYear offset := packedBits(hanQingJieQiFirstPacked, yearIndex*4, 4) - 4 patternID := packedBits(hanQingJieQiPatternIndexPacked, yearIndex*7, 7) if patternID >= hanQingJieQiPatternCount { return time.Time{}, fmt.Errorf("历法节气表数据异常") } for i := 0; i < termIndex; i++ { offset += hanQingJieQiPatternDelta(patternID, i) } baseJDN := int(math.Floor(basic.JDECalc(rowYear-1, 12, 31) + 0.5)) return basic.JDE2DateByZone(float64(baseJDN+offset)-0.5, getCst(), true), nil } func calendricalJieQiTableTermIndex(term int) (int, error) { if term < 0 || term >= 360 || term%15 != 0 { return 0, fmt.Errorf("节气参数超出范围") } return ((term - JQ_小寒 + 360) % 360) / 15, nil } func hanQingJieQiPatternDelta(patternID, pos int) int { key := patternID*hanQingJieQiPatternLength + pos if delta, ok := hanQingJieQiPatternExceptionDelta(key); ok { return delta } if packedBits(hanQingJieQiPatternBits, key, 1) == 1 { return 16 } return 15 } // hanQingJieQiPatternExceptions 是未排序的 95 项例外表,先缓存成 map 再查,避免每次线性扫描。 var ( hanQingJieQiPatternExceptionOnce sync.Once hanQingJieQiPatternExceptionByKey map[int]int ) func hanQingJieQiPatternExceptionDelta(key int) (int, bool) { hanQingJieQiPatternExceptionOnce.Do(func() { m := make(map[int]int, len(hanQingJieQiPatternExceptions)) for _, item := range hanQingJieQiPatternExceptions { k := int(item & 0x0FFF) if _, exists := m[k]; exists { continue } switch item >> 12 { case 0: m[k] = 12 case 1: m[k] = 14 case 2: m[k] = 17 default: m[k] = 15 } } hanQingJieQiPatternExceptionByKey = m }) delta, ok := hanQingJieQiPatternExceptionByKey[key] return delta, ok } func packedBits(data []byte, offset, width int) int { value := 0 for i := 0; i < width; i++ { bit := offset + i if data[bit/8]&(1<