bug fix && moon phase date calc

master
兔子 3 years ago
parent b4b2cea738
commit 9b50cfff3f
Signed by: b612
GPG Key ID: 481225A74DEB62A1

@ -207,13 +207,13 @@ func JDE2DateByZone(JD float64, tz *time.Location, byZone bool) time.Time {
return time.Unix(dates.Unix()+int64(tms), int64((tms-math.Floor(tms))*1000000000)) return time.Unix(dates.Unix()+int64(tms), int64((tms-math.Floor(tms))*1000000000))
} }
func GetLunar(year, month, day int) (lmonth, lday int, leap bool, result string) { func GetLunar(year, month, day int, tz float64) (lmonth, lday int, leap bool, result string) {
jde := JDECalc(year, month, float64(day)) //计算当前JDE时间 jde := JDECalc(year, month, float64(day)) //计算当前JDE时间
if month == 11 || month == 12 { //判断当前日期属于前一年周期还是后一年周期 if month == 11 || month == 12 { //判断当前日期属于前一年周期还是后一年周期
//判断方法:当前日期与冬至日所在朔望月的关系 //判断方法:当前日期与冬至日所在朔望月的关系
winterday := GetJQTime(year, 270) + 8.0/24.0 //冬至日日期(世界时,北京时间) winterday := GetJQTime(year, 270) + tz //冬至日日期(世界时,北京时间)
Fday := TD2UT(CalcMoonS(float64(year)+11.0/12.0+5.0/30.0/12.0, 0), true) + 8.0/24.0 //朔月(世界时,北京时间) Fday := TD2UT(CalcMoonS(float64(year)+11.0/12.0+5.0/30.0/12.0, 0), true) + tz //朔月(世界时,北京时间)
Yday := TD2UT(CalcMoonS(float64(year)+1.0, 0), true) + 8.0/24.0 //下一朔月(世界时,北京时间) Yday := TD2UT(CalcMoonS(float64(year)+1.0, 0), true) + tz //下一朔月(世界时,北京时间)
if Fday-math.Floor(Fday) > 0.5 { if Fday-math.Floor(Fday) > 0.5 {
Fday = math.Floor(Fday) + 0.5 Fday = math.Floor(Fday) + 0.5
} else { } else {
@ -238,6 +238,9 @@ func GetLunar(year, month, day int) (lmonth, lday int, leap bool, result string)
winter1 := jieqi[1] //第一年冬至日 winter1 := jieqi[1] //第一年冬至日
winter2 := jieqi[25] //第二年冬至日 winter2 := jieqi[25] //第二年冬至日
for k, v := range moon { for k, v := range moon {
if tz != 8.0/24 {
v = v - 8.0/24 + tz
}
if v-math.Floor(v) > 0.5 { if v-math.Floor(v) > 0.5 {
moon[k] = math.Floor(v) + 0.5 moon[k] = math.Floor(v) + 0.5
} else { } else {
@ -245,6 +248,9 @@ func GetLunar(year, month, day int) (lmonth, lday int, leap bool, result string)
} }
} //置闰月为0点 } //置闰月为0点
for k, v := range jieqi { for k, v := range jieqi {
if tz != 8.0/24 {
v = v - 8.0/24 + tz
}
if v-math.Floor(v) > 0.5 { if v-math.Floor(v) > 0.5 {
jieqi[k] = math.Floor(v) + 0.5 jieqi[k] = math.Floor(v) + 0.5
} else { } else {
@ -298,6 +304,9 @@ func GetLunar(year, month, day int) (lmonth, lday int, leap bool, result string)
if sleap { if sleap {
lmonth-- lmonth--
} }
if lmonth <= 0 {
lmonth += 12
}
lday = int(jde-moon[i-1]) + 1 lday = int(jde-moon[i-1]) + 1
strmonth := []string{"十", "一", "二", "三", "四", "五", "六", "七", "八", "九", "十", "冬", "腊"} strmonth := []string{"十", "一", "二", "三", "四", "五", "六", "七", "八", "九", "十", "冬", "腊"}
strday := []string{"初", "十", "廿", "三"} strday := []string{"初", "十", "廿", "三"}
@ -319,7 +328,7 @@ func GetLunar(year, month, day int) (lmonth, lday int, leap bool, result string)
return return
} }
func GetSolar(year, month, day int, leap bool) float64 { func GetSolar(year, month, day int, leap bool, tz float64) float64 {
if month < 11 { if month < 11 {
year-- year--
} }
@ -328,6 +337,9 @@ func GetSolar(year, month, day int, leap bool) float64 {
winter1 := jieqi[1] //第一年冬至日 winter1 := jieqi[1] //第一年冬至日
winter2 := jieqi[25] //第二年冬至日 winter2 := jieqi[25] //第二年冬至日
for k, v := range moon { for k, v := range moon {
if tz != 8.0/24 {
v = v - 8.0/24 + tz
}
if v-math.Floor(v) > 0.5 { if v-math.Floor(v) > 0.5 {
moon[k] = math.Floor(v) + 0.5 moon[k] = math.Floor(v) + 0.5
} else { } else {
@ -335,6 +347,9 @@ func GetSolar(year, month, day int, leap bool) float64 {
} }
} //置闰月为0点 } //置闰月为0点
for k, v := range jieqi { for k, v := range jieqi {
if tz != 8.0/24 {
v = v - 8.0/24 + tz
}
if v-math.Floor(v) > 0.5 { if v-math.Floor(v) > 0.5 {
jieqi[k] = math.Floor(v) + 0.5 jieqi[k] = math.Floor(v) + 0.5
} else { } else {
@ -354,6 +369,17 @@ func GetSolar(year, month, day int, leap bool) float64 {
mooncount++ mooncount++
} }
} }
leapmonth := 20
if mooncount == 13 { //存在闰月
j, i := 3, 1
for i = min; i <= max; i++ {
if !(moon[i] <= jieqi[j] && moon[i+1] > jieqi[j]) {
break
}
j += 2
}
leapmonth = i - min + 1
}
if leap { if leap {
month++ month++
} }
@ -362,6 +388,9 @@ func GetSolar(year, month, day int, leap bool) float64 {
} else { } else {
month++ month++
} }
if month >= leapmonth && !leap {
month++
}
jde := moon[min-1+month] + float64(day) - 1 jde := moon[min-1+month] + float64(day) - 1
return jde return jde
} }

@ -1106,36 +1106,40 @@ func MoonLight(JD float64) float64 {
return k return k
} }
func SunMoonSeek(JDE float64) float64 { func SunMoonSeek(JDE float64, degree float64) float64 {
p := HMoonSeeLo(JDE) - (HSunSeeLo(JDE)) p := HMoonSeeLo(JDE) - (HSunSeeLo(JDE)) - degree
if p > 240 { for p < -180 {
p -= 360
}
if p < -240 {
p += 360 p += 360
} }
for p > 180 {
p -= 360
}
return p return p
} }
func CalcMoonSH(Year float64, C int) float64 { func CalcMoonSHByJDE(JDE float64, C int) float64 {
JDE := CalcMoonS(Year, C)
C = C * 180 C = C * 180
i := 0 JD1 := JDE
for { for {
JDE -= 0.005 JD0 := JD1
i++ stDegree := SunMoonSeek(JD0, float64(C))
if i > 1000 { stDegreep := (SunMoonSeek(JD0+0.000005, float64(C)) - SunMoonSeek(JD0-0.000005, float64(C))) / 0.00001
JD1 = JD0 - stDegree/stDegreep
if math.Abs(JD1-JD0) <= 0.00001 {
break break
} }
if SunMoonSeek(JDE) <= float64(C) {
break
} }
return JD1
} }
func CalcMoonSH(Year float64, C int) float64 {
JDE := CalcMoonS(Year, C)
C = C * 180
JD1 := JDE JD1 := JDE
for { for {
JD0 := JD1 JD0 := JD1
stDegree := SunMoonSeek(JD0) - float64(C) stDegree := SunMoonSeek(JD0, float64(C))
stDegreep := (SunMoonSeek(JD0+0.000005) - SunMoonSeek(JD0-0.000005)) / 0.00001 stDegreep := (SunMoonSeek(JD0+0.000005, float64(C)) - SunMoonSeek(JD0-0.000005, float64(C))) / 0.00001
JD1 = JD0 - stDegree/stDegreep JD1 = JD0 - stDegree/stDegreep
if math.Abs(JD1-JD0) <= 0.00001 { if math.Abs(JD1-JD0) <= 0.00001 {
break break
@ -1196,29 +1200,37 @@ func CalcMoonS(Year float64, C int) float64 {
return JDE return JDE
} }
func CalcMoonXH(Year float64, C int) float64 { func CalcMoonXHByJDE(JDE float64, C int) float64 {
JDE := CalcMoonX(Year, C)
if C == 0 { if C == 0 {
C = 90 C = 90
} else { } else {
C = -90 C = -90
} }
i := 0 JD1 := JDE
for { for {
JDE -= 0.005 JD0 := JD1
i++ stDegree := SunMoonSeek(JD0, float64(C))
if i > 1000 { stDegreep := (SunMoonSeek(JD0+0.000005, float64(C)) - SunMoonSeek(JD0-0.000005, float64(C))) / 0.00001
JD1 = JD0 - stDegree/stDegreep
if math.Abs(JD1-JD0) <= 0.00001 {
break break
} }
if SunMoonSeek(JDE) <= float64(C) {
break
} }
return JD1
}
func CalcMoonXH(Year float64, C int) float64 {
JDE := CalcMoonX(Year, C)
if C == 0 {
C = 90
} else {
C = -90
} }
JD1 := JDE JD1 := JDE
for { for {
JD0 := JD1 JD0 := JD1
stDegree := SunMoonSeek(JD0) - float64(C) stDegree := SunMoonSeek(JD0, float64(C))
stDegreep := (SunMoonSeek(JD0+0.000005) - SunMoonSeek(JD0-0.000005)) / 0.00001 stDegreep := (SunMoonSeek(JD0+0.000005, float64(C)) - SunMoonSeek(JD0-0.000005, float64(C))) / 0.00001
JD1 = JD0 - stDegree/stDegreep JD1 = JD0 - stDegree/stDegreep
if math.Abs(JD1-JD0) <= 0.00001 { if math.Abs(JD1-JD0) <= 0.00001 {
break break

@ -37,7 +37,7 @@ const (
// 传入 公历年月日 // 传入 公历年月日
// 返回 农历月,日,是否闰月以及文字描述 // 返回 农历月,日,是否闰月以及文字描述
func Lunar(year, month, day int) (int, int, bool, string) { func Lunar(year, month, day int) (int, int, bool, string) {
return basic.GetLunar(year, month, day) return basic.GetLunar(year, month, day, 8.0/24.0)
} }
// ChineseLunar 公历转农历 // ChineseLunar 公历转农历
@ -45,7 +45,7 @@ func Lunar(year, month, day int) (int, int, bool, string) {
// 返回 农历月,日,是否闰月以及文字描述 // 返回 农历月,日,是否闰月以及文字描述
// 忽略时区,日期一律按北京时间计算 // 忽略时区,日期一律按北京时间计算
func ChineseLunar(date time.Time) (int, int, bool, string) { func ChineseLunar(date time.Time) (int, int, bool, string) {
return basic.GetLunar(date.Year(), int(date.Month()), date.Day()) return basic.GetLunar(date.Year(), int(date.Month()), date.Day(), 8.0/24.0)
} }
// Solar 农历转公历 // Solar 农历转公历
@ -55,7 +55,7 @@ func ChineseLunar(date time.Time) (int, int, bool, string) {
// 例计算己亥猪年腊月三十日对应的公历即2020年1月24日 // 例计算己亥猪年腊月三十日对应的公历即2020年1月24日
// 由于农历还未到鼠年故应当传入Solar(2019,12,30,false) // 由于农历还未到鼠年故应当传入Solar(2019,12,30,false)
func Solar(year, month, day int, leap bool) time.Time { func Solar(year, month, day int, leap bool) time.Time {
jde := basic.GetSolar(year, month, day, leap) jde := basic.GetSolar(year, month, day, leap, 8.0/24.0)
zone := time.FixedZone("CST", 8*3600) zone := time.FixedZone("CST", 8*3600)
return basic.JDE2DateByZone(jde, zone, true) return basic.JDE2DateByZone(jde, zone, true)
} }

@ -5,7 +5,50 @@ import (
"testing" "testing"
) )
func Test_Solar(t *testing.T) { type LunarSolar struct {
fmt.Println(Solar(2021, 1, 1, false)) Lyear int
fmt.Println(Solar(2020, 1, 1, false)) Lmonth int
Lday int
Leap bool
Year int
Month int
Day int
}
func Test_ChineseCalendar(t *testing.T) {
var testData = []LunarSolar{
{Lyear: 2034, Lmonth: 1, Lday: 1, Leap: false, Year: 2034, Month: 2, Day: 19},
{Lyear: 2033, Lmonth: 12, Lday: 30, Leap: false, Year: 2034, Month: 2, Day: 18},
{Lyear: 2033, Lmonth: 11, Lday: 27, Leap: true, Year: 2034, Month: 1, Day: 17},
{Lyear: 2033, Lmonth: 11, Lday: 1, Leap: true, Year: 2033, Month: 12, Day: 22},
{Lyear: 2033, Lmonth: 11, Lday: 30, Leap: false, Year: 2033, Month: 12, Day: 21},
{Lyear: 2023, Lmonth: 2, Lday: 30, Leap: false, Year: 2023, Month: 3, Day: 21},
{Lyear: 2023, Lmonth: 2, Lday: 1, Leap: true, Year: 2023, Month: 3, Day: 22},
{Lyear: 2020, Lmonth: 1, Lday: 1, Leap: false, Year: 2020, Month: 1, Day: 25},
{Lyear: 2015, Lmonth: 1, Lday: 1, Leap: false, Year: 2015, Month: 2, Day: 19},
{Lyear: 2014, Lmonth: 12, Lday: 30, Leap: false, Year: 2015, Month: 2, Day: 18},
{Lyear: 1996, Lmonth: 1, Lday: 1, Leap: false, Year: 1996, Month: 2, Day: 19},
{Lyear: 1995, Lmonth: 12, Lday: 30, Leap: false, Year: 1996, Month: 2, Day: 18},
{Lyear: 1996, Lmonth: 10, Lday: 30, Leap: false, Year: 1996, Month: 12, Day: 10},
{Lyear: 2014, Lmonth: 9, Lday: 1, Leap: true, Year: 2014, Month: 10, Day: 24},
{Lyear: 2014, Lmonth: 9, Lday: 30, Leap: false, Year: 2014, Month: 10, Day: 23},
{Lyear: 2014, Lmonth: 10, Lday: 1, Leap: false, Year: 2014, Month: 11, Day: 22},
{Lyear: 2021, Lmonth: 12, Lday: 29, Leap: false, Year: 2022, Month: 1, Day: 31},
}
for _, v := range testData {
var lyear int = v.Year
lmonth, lday, leap, desp := Lunar(v.Year, v.Month, v.Day)
if lmonth > v.Month {
lyear--
}
fmt.Println(lyear, desp, v.Year, v.Month, v.Day)
if lyear != v.Lyear || lmonth != v.Lmonth || lday != v.Lday || leap != v.Leap {
t.Fatal(v, lyear, lmonth, lday, leap, desp)
}
date := Solar(v.Lyear, v.Lmonth, v.Lday, v.Leap)
if date.Year() != v.Year || int(date.Month()) != v.Month || date.Day() != v.Day {
t.Fatal(v, date)
}
}
} }

@ -2,6 +2,7 @@ package moon
import ( import (
"errors" "errors"
"math"
"time" "time"
"b612.me/astro/basic" "b612.me/astro/basic"
@ -180,24 +181,137 @@ func ShuoYue(year float64) time.Time {
return basic.JDE2DateByZone(jde, time.UTC, false) return basic.JDE2DateByZone(jde, time.UTC, false)
} }
func NextShuoYue(date time.Time) time.Time {
return nextMoonPhase(date, 0)
}
func LastShuoYue(date time.Time) time.Time {
return lastMoonPhase(date, 0)
}
func ClosestShuoYue(date time.Time) time.Time {
return closestMoonPhase(date, 0)
}
func closestMoonPhase(date time.Time, typed int) time.Time {
//0=shuo 1=wang 2=shangxian 3=xiaxian
jde := basic.TD2UT(basic.Date2JDE(date.UTC()), true)
if typed < 2 {
return basic.JDE2DateByZone(basic.TD2UT(basic.CalcMoonSHByJDE(jde, typed), false), date.Location(), false)
}
return basic.JDE2DateByZone(basic.TD2UT(basic.CalcMoonXHByJDE(jde, typed-2), false), date.Location(), false)
}
func nextMoonPhase(date time.Time, typed int) time.Time {
//0=shuo 1=wang 2=shangxian 3=xiaxian
diffCode := 0.00
switch typed {
case 1:
diffCode = 180
case 2:
diffCode = 90
case 3:
diffCode = 270
}
jde := basic.TD2UT(basic.Date2JDE(date.UTC()), true)
cost := basic.HMoonSeeLo(jde) - basic.HSunSeeLo(jde) - float64(diffCode)
for cost < 0 {
cost += 360
}
if cost < 0 && math.Floor(math.Abs(cost)*10000) == 0 {
cost = 0
}
if cost < 240 {
jde += (240 - cost) / 11.19
}
if typed < 2 {
return basic.JDE2DateByZone(basic.TD2UT(basic.CalcMoonSHByJDE(jde, typed), false), date.Location(), false)
}
return basic.JDE2DateByZone(basic.TD2UT(basic.CalcMoonXHByJDE(jde, typed-2), false), date.Location(), false)
}
func lastMoonPhase(date time.Time, typed int) time.Time {
//0=shuo 1=wang 2=shangxian 3=xiaxian
diffCode := 0.00
switch typed {
case 1:
diffCode = 180
case 2:
diffCode = 90
case 3:
diffCode = 270
}
jde := basic.TD2UT(basic.Date2JDE(date.UTC()), true)
cost := basic.HMoonSeeLo(jde) - basic.HSunSeeLo(jde) - float64(diffCode)
for cost < 0 {
cost += 360
}
if cost > 0 && math.Floor(math.Abs(cost)*10000) == 0 {
cost = 360
}
if cost > 120 {
jde -= (cost - 120) / 11.19
}
if typed < 2 {
return basic.JDE2DateByZone(basic.TD2UT(basic.CalcMoonSHByJDE(jde, typed), false), date.Location(), false)
}
return basic.JDE2DateByZone(basic.TD2UT(basic.CalcMoonXHByJDE(jde, typed-2), false), date.Location(), false)
}
// WangYue 望月 // WangYue 望月
func WangYue(year float64) time.Time { func WangYue(year float64) time.Time {
jde := basic.TD2UT(basic.CalcMoonSH(year, 1), false) jde := basic.TD2UT(basic.CalcMoonSH(year, 1), false)
return basic.JDE2DateByZone(jde, time.UTC, false) return basic.JDE2DateByZone(jde, time.UTC, false)
} }
func NextWangYue(date time.Time) time.Time {
return nextMoonPhase(date, 1)
}
func LastWangYue(date time.Time) time.Time {
return lastMoonPhase(date, 1)
}
func ClosestWangYue(date time.Time) time.Time {
return closestMoonPhase(date, 1)
}
// ShangXianYue 上弦月 // ShangXianYue 上弦月
func ShangXianYue(year float64) time.Time { func ShangXianYue(year float64) time.Time {
jde := basic.TD2UT(basic.CalcMoonXH(year, 0), false) jde := basic.TD2UT(basic.CalcMoonXH(year, 0), false)
return basic.JDE2DateByZone(jde, time.UTC, false) return basic.JDE2DateByZone(jde, time.UTC, false)
} }
func NextShangXianYue(date time.Time) time.Time {
return nextMoonPhase(date, 2)
}
func LastShangXianYue(date time.Time) time.Time {
return lastMoonPhase(date, 2)
}
func ClosestShangXianYue(date time.Time) time.Time {
return closestMoonPhase(date, 2)
}
// XiaXianYue 下弦月 // XiaXianYue 下弦月
func XiaXianYue(year float64) time.Time { func XiaXianYue(year float64) time.Time {
jde := basic.TD2UT(basic.CalcMoonXH(year, 1), false) jde := basic.TD2UT(basic.CalcMoonXH(year, 1), false)
return basic.JDE2DateByZone(jde, time.UTC, false) return basic.JDE2DateByZone(jde, time.UTC, false)
} }
func NextXiaXianYue(date time.Time) time.Time {
return nextMoonPhase(date, 3)
}
func LastXiaXianYue(date time.Time) time.Time {
return lastMoonPhase(date, 3)
}
func ClosestXiaXianYue(date time.Time) time.Time {
return closestMoonPhase(date, 3)
}
// EarthDistance 日地距离 // EarthDistance 日地距离
// 返回date对应UTC世界时日地距离 // 返回date对应UTC世界时日地距离
func EarthDistance(date time.Time) float64 { func EarthDistance(date time.Time) float64 {

@ -6,6 +6,49 @@ import (
"time" "time"
) )
func Test_Rise(t *testing.T) { func Test_MoonPhaseDate(t *testing.T) {
fmt.Println(RiseTime(time.Now(), 120, 40, 10, true)) //指定北京时间2022年1月20日
tz := time.FixedZone("CST", 8*3600)
date := time.Date(2022, 01, 20, 00, 00, 00, 00, tz)
//指定日期后的下一个朔月
moonPhase01 := NextShuoYue(date)
fmt.Println("下一朔月", moonPhase01)
if moonPhase01.Unix() != 1643694349 {
t.Fatal(moonPhase01)
}
//指定日期后的上一个朔月
moonPhase01 = LastShuoYue(date)
fmt.Println("上一朔月", moonPhase01)
if moonPhase01.Unix() != 1641148399 {
t.Fatal(moonPhase01)
}
//离指定日期最近的朔月
moonPhase01 = ClosestShuoYue(date)
fmt.Println("最近朔月", moonPhase01)
if moonPhase01.Unix() != 1643694349 {
t.Fatal(moonPhase01)
}
//离指定日期最近的望月时间
moonPhase01 = ClosestWangYue(date)
fmt.Println("最近望月", moonPhase01)
if moonPhase01.Unix() != 1642463294 {
t.Fatal(moonPhase01)
}
//离指定日期最近的上弦月时间
moonPhase01 = ClosestShangXianYue(date)
fmt.Println("最近上弦月", moonPhase01)
if moonPhase01.Unix() != 1641751864 {
t.Fatal(moonPhase01)
}
//离指定日期最近的下弦月时间
moonPhase01 = ClosestXiaXianYue(date)
fmt.Println("最近下弦月", moonPhase01)
if moonPhase01.Unix() != 1643118043 {
t.Fatal(moonPhase01)
}
//-------------------
for i := 0; i < 26; i++ {
moonPhase01 = LastShuoYue(moonPhase01)
fmt.Println("上一朔月", moonPhase01)
}
} }

Loading…
Cancel
Save