astro/calendar/chineseHantoQing.go

493 lines
37 KiB
Go
Raw Normal View History

package calendar
import (
"errors"
"fmt"
"strings"
"time"
)
var ERR_NIANHAO_NOT_FOUND = errors.New("ERROR:未找到对应的年号")
func getHanQingCals() []uint32 {
return []uint32{
2865769984, 1431610368, 3310368000, 1788881408, 2874351360, 2865771008, 1431316224, 1789500416, 1520447232, 2866160640, 2862626304, 3578800896, 1521262848, 1436562432, 2863671296, 1431477760, 3041931264, 1436558848, 2863700480, 2862624000, 3042255360, 2907714816, 1432365568, 2863374080, 1788883456, 2907711232, 1432984576, 1431318272, 2863506432, 1520449280, 2865769216, 1432101120, 1431319552, 1520445696, 2874318080, 2863673344, 1431315968, 1520999168, 1453337856, 2863964672, 2862626048, 3578800640, 1454087936, 1432367616, 2862622464, 3578928128, 2907713024, 1437475840, 1431320320, 2862623744, 2907971584, 2874159872, 1431316736, 2863308288, 1788882944, 2874156288, 1431902976, 1431318016, 1789731328, 1453339904, 2865768960, 1431511040, 3578802432, 1453336320, 2865863680, 2862624512, 3579192064, 3041932800, 1436560384, 2863407360, 2862625536, 3041929216, 1437212160, 1432367360, 2862621952, 3041958400, 2874158336, 1433215744, 1431319808, 1788881408, 2907905792, 2865771008, 1431316224, 1789500416, 1520447232, 2866160640, 2863674880, 1431317504, 1521262848, 1436562432, 2863671296, 1431477760, 3041931264, 1453336064, 2863700480, 2862624000, 3042255360, 2907714816, 1432365568, 2863374080, 2862625280, 2907711232, 1432984576, 1431318272, 2863506432, 1520449280, 2874157824, 1432101120, 1431319552, 1520445696, 2874318080, 2863673344, 1431315968, 1789434624, 1453337856, 2863964672, 2862626048, 3578800640, 1454087936, 1436561920, 2862622464, 3578928128, 2907713024, 1437475840, 1431320320, 2862623744, 3042189312, 2874159872, 1431316736, 2863308288, 1788882944, 2874156288, 1432951552, 1431318016, 1789731328, 1453339904, 2865768960, 1431511040, 3578802432, 1520445184, 2865863680, 2862624512, 3579192064, 3041932800, 1436560384, 2863931648, 2862625536, 3041929216, 1437212160, 1432367360, 2862621952, 3041958400, 2907712768, 1433215744, 1431319808, 1788881408, 2907905792, 2865771008, 1431316224, 2863242240, 1520447232, 2866160640, 2863674880, 1431317504, 1521262848, 1453339648, 2863671296, 1431477760, 3041931264, 1453336064, 2863700480, 2862624000, 3579126272, 2907714816, 1432365568, 2863374080, 2862625280, 2907711232, 1437178880, 1431318272, 2863506432, 1520449280, 2874157824, 1432101120, 1431319552, 1788881152, 2874318080, 2863673344, 1431315968, 1788910336, 2862623744, 3042255104, 2874160128, 1432365568, 2863373824, 1788883200, 2907710976, 1432984576, 1431318016, 1789764352, 1520449024, 2865769216, 1432100864, 1431319296, 1520445440, 2865929472, 2863673088, 1431315712, 1520998912, 1453337856, 2863964416, 2862625792, 3041929472, 1454087936, 1432367360, 2862622208, 3578927872, 2907713024, 1433281280, 1431320064, 2862623488, 2907971584, 2874159616, 1431316480, 2863308032, 1520447488, 2874156032, 1431902720, 1431317760, 1789731328, 1453339648, 2863671552, 1431510784, 3578802432, 1453336064, 2865863424, 2862624256, 3579192064, 2907714816, 1436560128, 2863407104, 2862625536, 3041928960, 1437211904, 1431318528, 2862621952, 3041958144, 2874158080, 1433215488, 1431319808, 1788881152, 2874351104, 2865770752, 1431316224, 1789500160, 1453338112, 2866160384, 2862626304, 3578800640, 1521262592, 1436562176, 2862622720, 3578960896, 3041931008, 1436558592, 2863700480, 2862623744, 3042255104, 2874160128, 1432365568, 2863373824, 1788883200, 2907710976, 1432984576, 1431318016, 1789764352, 1520449024, 2865769216, 1432100864, 1431319296, 1520445440, 2865929472, 2863673088, 1431315712, 1520998912, 1453337856, 2863964416, 2862625792, 3041929472, 1454087936, 1432367360, 2862622208, 3578927872, 2907713024, 1433281280, 1431320064, 2862623488, 2907971584, 2874159616, 1431316480, 2863308032, 1520447488, 2874156032, 1431902720, 1431317760, 1789731328, 1453339648, 2863671552, 1431510784, 3578802432, 1453336064, 2865863424, 2862624256, 3579192064, 2907714816, 1436560128, 2863407104, 2862625536, 3041928960, 1437211904, 1431318528, 2862621952, 3041958144, 2874158080, 1433215488, 1431319808, 1788881152, 2874351104, 2865770752, 1431316224, 1789500160, 1453338112, 2866160384, 2862626304, 3578800640, 1521262592, 1436562176, 2862622720, 3578960896, 3041931008, 1436558592, 2
}
}
// 处理公元-105-1912年间的农历
// 返回:农历年,农历月,农历月干支(闰月从上月),农历日,是否闰月,农历日期字符串
func rapidLunarHan2Qing(year, month, day int, diff int, options func() map[int]uint32) (int, int, string, int, bool, string) {
useGoto := false
beijingTime := time.FixedZone("CST", 8*3600)
recalc:
idx := year + 105
magic := getHanQingCals()[idx]
if options != nil {
if v, ok := options()[year]; ok {
magic = v
}
}
springDays := int8((magic >> 7)) >> 1
if springDays>>6 == 1 {
springDays = -(springDays << 2 >> 2)
}
springDate := time.Date(year-1, 12, 31, 0, 0, 0, 0, beijingTime).AddDate(0, 0, int(springDays))
if !useGoto && (int(springDate.Month()) > month || ((int(springDate.Month()) == month) && springDate.Day() > day)) {
year--
useGoto = true
goto recalc
}
calcYear := year
if useGoto {
calcYear++
}
target := time.Date(calcYear, time.Month(month), day, 0, 0, 0, 0, getCst())
diffDay := int(target.Sub(springDate).Hours() / 24)
//go语言在 1582年10月4日前使用的是逆推格里高利历与实际使用的儒略历有所不同主要体现在百年闰年计算上
//儒略历修正
if springDate.Year()%100 == 0 && springDate.Year()%400 != 0 && springDate.Year() < 1582 && (target.Month() >= 3 || (target.Year() > springDate.Year())) {
diffDay++
}
//儒略历转格里高历修正
if calcYear == 1582 && ((month == 10 && day >= 15) || month > 10) {
diffDay -= 10
}
if calcYear == 1583 && month == 1 && day <= 23 {
diffDay -= 10
}
lunarMonth := 1
totalDay := 0
isLeap := false
leapMonth := int(uint8(magic>>15) & 0xF)
for i := 0; i < 13; i++ {
var dayofLunar = 29
if uint8(magic>>(31-i))&1 == 1 {
dayofLunar++
}
if totalDay+dayofLunar > diffDay {
lday := diffDay - totalDay + 1
format := formatLunarDateString(lunarMonth, lday, isLeap, diff)
ganzhiOfMonth := commonGanZhiOfMonth(year, lunarMonth)
if diff == 255 {
diff = 2
}
if diff != 0 {
lunarMonth += diff
if lunarMonth > 12 {
lunarMonth -= 12
year++
}
}
return year, lunarMonth, ganzhiOfMonth, lday, isLeap, format
}
totalDay += dayofLunar
lunarMonth++
if lunarMonth-leapMonth == 1 && !isLeap {
isLeap = true
lunarMonth--
} else {
isLeap = false
}
}
return 0, 0, "", 0, false, "无法获取农历信息"
}
func rapidSolarHan2Qing(year, month, day int, isLeap bool, diff int, options func() map[int]uint32) time.Time {
useGoto := false
beijingTime := time.FixedZone("CST", 8*3600)
recalc:
idx := year + 105
magic := getHanQingCals()[idx]
if options != nil {
if v, ok := options()[year]; ok {
magic = v
}
}
springDays := int8((magic >> 7)) >> 1
if springDays>>6 == 1 {
springDays = -(springDays << 2 >> 2)
}
springDate := time.Date(year-1, 12, 31, 0, 0, 0, 0, beijingTime).AddDate(0, 0, int(springDays))
if diff == 255 {
diff = 2
}
if diff > 0 && !useGoto {
month = month - diff
if month <= 0 {
month += 12
year--
useGoto = true
goto recalc
}
}
lunarMonth := 1
totalDay := 0
leap := false
leapMonth := int(uint8(magic>>15) & 0xF)
for i := 0; i < 13; i++ {
if lunarMonth == month && isLeap == leap {
target := springDate.AddDate(0, 0, totalDay+day-1)
if target.Year() == 1582 && ((target.Month() == 10 && target.Day() > 4) || target.Month() > 10) {
target = target.AddDate(0, 0, 10)
}
//go语言在 1582年10月4日前使用的是逆推格里高利历与实际使用的儒略历有所不同主要体现在百年闰年计算上
//儒略历修正
if springDate.Year()%100 == 0 && springDate.Year()%400 != 0 && springDate.Year() < 1582 && (target.Month() >= 3 || (target.Year() > springDate.Year())) {
target = target.AddDate(0, 0, -1)
}
return target
}
var dayofLunar = 29
if uint8(magic>>(31-i))&1 == 1 {
dayofLunar++
}
totalDay += dayofLunar
lunarMonth++
if lunarMonth-leapMonth == 1 && !leap {
leap = true
lunarMonth--
} else {
leap = false
}
}
return time.Time{}
}
func yearDiff(year, month, day int) int {
// 王莽改制公元9年-22年建丑为正月
// 注意公元23年恢复建寅为正月但是当年还是新朝地皇四年如果在新朝的角度当年还是建丑为正月
if year == 9 && month == 1 && day < 15 {
return 0
}
if year == 23 && month == 12 && day == 31 {
return 0
}
if year >= 9 && year <= 23 {
return 1
}
//魏明帝改制公元237年-240年建丑为正月
if year == 237 && month > 4 {
return 1
}
if year == 237 && month == 4 && day >= 12 {
return 1
}
if year > 237 && year < 240 {
return 1
}
if year == 240 && month == 1 && day < 12 {
return 1
}
//武则天改制公元690年-700年建子为正月
if year == 689 && month == 12 && day >= 18 {
return 255
}
if year >= 690 && year < 700 {
return 255
}
if year == 700 && (month < 12 || month == 12 && day <= 14) {
return 255
}
//唐肃宗改制公元761-762年建寅为正月
if year == 761 && month == 12 && day >= 2 {
return 2
}
if year == 762 && month < 4 {
return 2
}
if year == 762 && month == 4 && day < 29 {
return 2
}
return 0
}
func yearDiffLunar(year, month, day int) int {
// 王莽改制公元9年-22年建丑为正月
// 注意公元23年恢复建寅为正月但是当年还是新朝地皇四年如果在新朝的角度当年还是建丑为正月
if year >= 9 && year <= 23 {
return 1
}
//魏明帝改制公元237年-240年建丑为正月
if year == 237 && month >= 4 {
return 1
}
if year > 237 && year < 240 {
return 1
}
//武则天改制公元690年-700年建子为正月
if year >= 690 && year < 700 {
return 255
}
//有两个12月
if year == 700 && month != 11 {
return 255
}
//唐肃宗改制公元761-762年建寅为正月
if year == 762 && month <= 5 {
return 2
}
return 0
}
func formatLunarDateString(lunarMonth, lunarDay int, isLeap bool, diff int) string {
monthNames := []string{"十", "正", "二", "三", "四", "五", "六", "七", "八", "九", "十", "冬", "腊"}
dayNames := []string{"十", "一", "二", "三", "四", "五", "六", "七", "八", "九", "十"}
dayPrefixes := []string{"初", "十", "廿", "三"}
switch diff {
case 1:
monthNames = []string{"十", "二", "三", "四", "五", "六", "七", "八", "九", "十", "冬", "腊", "正"}
case 2:
monthNames = []string{"十", "三", "四", "五", "六", "七", "八", "九", "十", "冬", "腊", "正", "二"}
case 3:
monthNames = []string{"十", "四", "五", "六", "七", "八", "九", "十", "冬", "腊", "正", "二", "三"}
case 255:
//武则天改制,将冬月成为正月,正月改为一月
monthNames = []string{"十", "一", "二", "三", "四", "五", "六", "七", "八", "九", "十", "正", "腊"}
}
var dateString string
if isLeap {
dateString += "闰"
}
dateString += monthNames[lunarMonth] + "月"
if lunarDay == 20 {
dateString += "二十"
} else if lunarDay == 10 {
dateString += "初十"
} else {
dateString += dayPrefixes[lunarDay/10] + dayNames[lunarDay%10]
}
return dateString
}
func innerSolarToLunarHanQing(date time.Time) Time {
year := date.Year()
month := int(date.Month())
day := date.Day()
yeardiff := yearDiff(year, month, day)
lyear, lmonth, ganzhiMonth, lday, isLeap, ldesc := rapidLunarHan2Qing(year, month, day, yeardiff, nil)
var eras []EraDesc
if lyear >= -103 && lyear <= 220 {
eras = innerEras(lyear, hanEras)
} else if lyear >= 221 && lyear <= 617 {
eras = innerEras(lyear, weiJinNanBeiChaoEras)
} else if lyear >= 618 && lyear <= 907 {
eras = innerEras(lyear, tangEras)
} else if lyear > 907 && lyear < 1368 {
eras = innerEras(lyear, wudaiSongYuanEras)
} else if lyear <= 1912 {
eras = innerEras(lyear, mingQingEras)
}
ldate := LunarTime{
solarDate: date,
year: lyear,
month: lmonth,
day: lday,
leap: isLeap,
desc: ldesc,
eras: eras,
ganzhiMonth: ganzhiMonth,
comment: "",
}
var otherLunars []LunarTime
//王莽特殊处理
if lyear == 23 {
liuxiu := ldate.eras[len(ldate.eras)-1:]
ldate.eras = ldate.eras[:1]
if month > 2 || (month == 2 && day >= 10) {
lyear, lmonth, ganzhiMonth, lday, isLeap, ldesc = rapidLunarHan2Qing(year, month, day, 0, nil)
otherLunars = append(otherLunars, LunarTime{
solarDate: date,
year: lyear,
month: lmonth,
day: lday,
leap: isLeap,
desc: ldesc,
eras: liuxiu,
ganzhiMonth: ganzhiMonth,
comment: "",
})
}
}
result := Time{
solarTime: date,
lunars: append([]LunarTime{ldate}, otherLunars...),
}
//三国时期
if year >= 220 && year <= 280 {
return innerSolarToLunarSanGuo(result)
}
//南北朝时期
if year >= 384 && year <= 589 {
return innerSolarToLunarNanBeiChao(result)
}
//五代十国辽金元
if year >= 947 && year <= 1279 {
return innerSolarToLunarLiaoJinYuan(result)
}
if year > 1644 && year < 1884 {
return innerSolarToLunarNanMing(result)
}
return result
}
func innerParseLunar(lunar string) ([]time.Time, error) {
date, err := parseChineseDate(lunar)
if err != nil {
return []time.Time{}, err
}
if date.year != 0 && date.comment == "" {
if date.year < -103 || date.year > 3000 {
return nil, fmt.Errorf("年份超出范围")
}
if date.year <= 1912 {
d := rapidSolarHan2Qing(date.year, date.month, date.day, date.leap, yearDiffLunar(date.year, date.month, date.day), nil)
return []time.Time{d}, nil
}
if date.year < 2400 {
dates := rapidSolarModern(date.year, date.month, date.day, date.leap)
return []time.Time{dates}, nil
}
dates := Solar(date.year, date.month, date.day, date.leap, 8.0)
return []time.Time{dates}, nil
}
data, err := innerLunar2SolarHanQing(date, nianHaoMap, nil)
if err != nil && err != ERR_NIANHAO_NOT_FOUND {
return data, err
}
if tmp, err := innerLunar2SolarHanQing(date, shuEraMap, shuCals); err == nil {
data = append(data, tmp...)
}
if tmp, err := innerLunar2SolarHanQing(date, wuEraMap, wuCals); err == nil {
data = append(data, tmp...)
}
if tmp, err := innerLunar2SolarHanQing(date, houQinEraMap, houQinCals); err == nil {
data = append(data, tmp...)
}
if tmp, err := innerLunar2SolarHanQing(date, weiZhouSuiEraMap, weiZhouSuiCals); err == nil {
data = append(data, tmp...)
}
if tmp, err := innerLunar2SolarHanQing(date, beiLiangEraMap, beiLiangCals); err == nil {
data = append(data, tmp...)
}
if tmp, err := innerLunar2SolarHanQing(date, dongWeiBeiQiEraMap, dongWeiBeiQiCals); err == nil {
data = append(data, tmp...)
}
if tmp, err := innerLunar2SolarHanQing(date, liaoJinYuanEraMap, liaoJinYuanCals); err == nil {
data = append(data, tmp...)
}
if tmp, err := innerLunar2SolarHanQing(date, nanMingEraMap, nanMingCals); err == nil {
data = append(data, tmp...)
}
return data, nil
}
func innerLunar2SolarHanQing(data LunarTime, nianHaoMap func() map[string][][]int, options func() map[int]uint32) ([]time.Time, error) {
isGanZhiDay := false
var tgIdx, dzIdex int
if data.ganzhiMonth != "" && data.day == 0 {
//日干支求解,先计算此月的初一,然后根据日干支求解
isGanZhiDay = true
data.day = 1
for idx, v := range tiangan {
if strings.HasPrefix(data.ganzhiMonth, v) {
tgIdx = idx
break
}
}
for idx, v := range dizhi {
if strings.HasSuffix(data.ganzhiMonth, v) {
dzIdex = idx
break
}
}
}
years := nianHaoMap()[data.comment]
if len(years) == 0 {
return []time.Time{}, ERR_NIANHAO_NOT_FOUND
}
var res []time.Time
for _, y := range years {
if y[1]-y[0]+1 < data.year {
continue
}
year := y[0] + data.year - 1
diff := yearDiffLunar(year, data.month, data.day)
//diff!=0且option!=nil时只有三国时期存在此时因为蜀汉和吴国都没有改制所以不进行处理
if options != nil {
diff = 0
}
if diff == 255 {
if year == 700 && data.month == 12 {
res = append(res, rapidSolarHan2Qing(year, data.month, data.day, data.leap, 0, options))
}
data.month = data.month + 2
if data.month > 12 {
data.month = data.month - 12
}
if data.month == 3 && strings.Contains(data.desc, "正月") {
data.month = 1
}
}
if year == 23 && data.comment == "更始" {
diff = 0
}
res = append(res, rapidSolarHan2Qing(year, data.month, data.day, data.leap, diff, options))
//当年两个12月
if year == 239 && data.month == 12 {
res = append(res, rapidSolarHan2Qing(year, data.month, data.day, data.leap, 0, options))
}
//当年两个4月5月
if year == 762 && (data.month == 4 || data.month == 5) {
res = append(res, rapidSolarHan2Qing(year, data.month, data.day, data.leap, 0, options))
}
}
var realRes []time.Time
for _, v := range res {
if v.IsZero() {
continue
}
if !isGanZhiDay {
realRes = append(realRes, v)
continue
}
//干支日求解
curTgIdx, curDzIdx := ganZhiOfDayIndex(v)
tgDiff := (tgIdx - curTgIdx + 10) % 10
dzDiff := (dzIdex - curDzIdx + 12) % 12
delta := (dzDiff - tgDiff + 12) % 12
delta2 := delta / 2
k := (delta2 * 5) % 6
d := tgDiff + 10*k
if d < 0 {
d += 60
}
if d > 30 {
continue
}
oldv := v
v = v.Add(time.Duration(d) * 24 * time.Hour)
if v.Year()%100 == 0 && v.Year()%400 != 0 && v.Month() >= 3 && v.Year() < 1582 {
if oldv.Month() < 3 {
v = v.AddDate(0, 0, 1)
}
}
if oldv.Year() == 1582 && ((oldv.Month() == 10 && oldv.Day() <= 4) || oldv.Month() < 10) {
if v.Month() > 10 || (v.Month() == 10 && v.Day() > 4) {
v = v.AddDate(0, 0, 10)
}
}
realRes = append(realRes, v)
}
return realRes, nil
}