diff --git a/basic/calendar.go b/basic/calendar.go index c3647e5..40917b6 100644 --- a/basic/calendar.go +++ b/basic/calendar.go @@ -197,10 +197,14 @@ func JDE2Date(JD float64) time.Time { Days = math.Floor(Days) tz, _ := time.LoadLocation("Local") dates := time.Date(int(Years), time.Month(int(Months)), int(Days), 0, 0, 0, 0, tz) - dates = time.Unix(dates.Unix()+int64(tms), int64((tms-math.Floor(tms))*1000000000)) - return dates + return time.Unix(dates.Unix()+int64(tms), int64((tms-math.Floor(tms))*1000000000)) } +// JDE2DateByZone JDE(儒略日)转日期 +// JD: 儒略日 +// tz: 目标时区 +// byZone: (true: 传入的儒略日视为目标时区当地时间的儒略日,false: 传入的儒略日视为UTC时间的儒略日) +// 回参:转换后的日期,时区始终为目标时区 func JDE2DateByZone(JD float64, tz *time.Location, byZone bool) time.Time { JD = JD + 0.5 Z := float64(int(JD)) @@ -231,12 +235,12 @@ func JDE2DateByZone(JD float64, tz *time.Location, byZone bool) time.Time { } tms := (Days - math.Floor(Days)) * 24 * 3600 Days = math.Floor(Days) + var transTz = tz if !byZone { - dates := time.Date(int(Years), time.Month(int(Months)), int(Days), 0, 0, 0, 0, time.UTC) - return time.Unix(dates.Unix()+int64(tms), int64((tms-math.Floor(tms))*1000000000)).In(tz) + transTz = time.UTC } - dates := time.Date(int(Years), time.Month(int(Months)), int(Days), 0, 0, 0, 0, tz) - return time.Unix(dates.Unix()+int64(tms), int64((tms-math.Floor(tms))*1000000000)) + return time.Date(int(Years), time.Month(int(Months)), int(Days), 0, 0, 0, 0, transTz). + Add(time.Duration(int64(1000000000 * tms))).In(tz) } func GetLunar(year, month, day int, tz float64) (lmonth, lday int, leap bool, result string) { diff --git a/calendar/chinese.go b/calendar/chinese.go index 985e1d7..7e96913 100644 --- a/calendar/chinese.go +++ b/calendar/chinese.go @@ -143,6 +143,9 @@ recalc: magic := int32(upper[idx])<<8 + int32(lower[idx]) springMonth := (magic&0x800000)>>23 + 1 springDay := (magic & 0x7FFFFF) >> 18 + if !useGoto && springMonth == int32(month) && springDay == int32(day) { + return 1, 1, false, "正月初一" + } if !useGoto && (springMonth > int32(month) || (springMonth == int32(month) && springDay > int32(day))) { year-- useGoto = true diff --git a/calendar/chinese_test.go b/calendar/chinese_test.go index 18f9ae4..f89ff90 100644 --- a/calendar/chinese_test.go +++ b/calendar/chinese_test.go @@ -18,6 +18,7 @@ type lunarSolar struct { func Test_ChineseCalendar(t *testing.T) { var testData = []lunarSolar{ + {Lyear: 1995, Lmonth: 12, Lday: 12, Leap: false, Year: 1996, Month: 1, Day: 31}, {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}, @@ -37,19 +38,37 @@ func Test_ChineseCalendar(t *testing.T) { {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 := SolarToLunar(time.Date(v.Year, time.Month(v.Month), v.Day, 0, 0, 0, 0, time.Local)) - 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) + { + var lyear int = v.Year + lmonth, lday, leap, desp := SolarToLunar(time.Date(v.Year, time.Month(v.Month), v.Day, 0, 0, 0, 0, time.Local)) + 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 := LunarToSolar(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) + } } + { + var lyear int = v.Year + lmonth, lday, leap, desp := RapidSolarToLunar(time.Date(v.Year, time.Month(v.Month), v.Day, 0, 0, 0, 0, time.Local)) + 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 := LunarToSolar(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) + date := RapidLunarToSolar(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) + } } } } diff --git a/mercury/mercury_test.go b/mercury/mercury_test.go index 44bdbfb..be6a641 100644 --- a/mercury/mercury_test.go +++ b/mercury/mercury_test.go @@ -7,8 +7,20 @@ import ( ) func TestMercury(t *testing.T) { - date := time.Now().Add(time.Hour * -24) - fmt.Println(CulminationTime(date, 115)) - fmt.Println(RiseTime(date, 115, 23, 0, false)) - fmt.Println(DownTime(date, 115, 23, 0, false)) + tz := time.FixedZone("CST", 8*3600) + date := time.Date(2022, 01, 20, 00, 00, 00, 00, tz) + if NextConjunction(date).Unix() != 1642933683 { + t.Fatal(NextConjunction(date).Unix()) + } + if CulminationTime(date, 115).Unix() != 1642654651 { + t.Fatal(CulminationTime(date, 115).Unix()) + } + date, err := (RiseTime(date, 115, 40, 0, false)) + if err != nil { + t.Fatal(err) + } + if date.Unix() != 1642636481 { + t.Fatal(date.Unix()) + } + fmt.Println(DownTime(date, 115, 40, 0, false)) } diff --git a/moon/moon.go b/moon/moon.go index 5c96c5c..b810112 100644 --- a/moon/moon.go +++ b/moon/moon.go @@ -209,11 +209,14 @@ func Phase(date time.Time) float64 { } // ShuoYue 朔月 +// 返回Date对应UTC世界时的月相大小 func ShuoYue(year float64) time.Time { jde := basic.TD2UT(basic.CalcMoonSH(year, 0), false) return basic.JDE2DateByZone(jde, time.UTC, false) } +// NextShuoYue 下次朔月时间 +// 返回date之后的下一个朔月时间(UTC时间) func NextShuoYue(date time.Time) time.Time { return nextMoonPhase(date, 0) } diff --git a/moon/moon_test.go b/moon/moon_test.go index 7b1a30e..a6e2c88 100644 --- a/moon/moon_test.go +++ b/moon/moon_test.go @@ -13,38 +13,38 @@ func Test_MoonPhaseDate(t *testing.T) { //指定日期后的下一个朔月 moonPhase01 := NextShuoYue(date) fmt.Println("下一朔月", moonPhase01) - if moonPhase01.Unix() != 1643694349 { - t.Fatal(moonPhase01) + if moonPhase01.Unix() != 1643694356 { + t.Fatal(moonPhase01.Unix()) } //指定日期后的上一个朔月 moonPhase01 = LastShuoYue(date) fmt.Println("上一朔月", moonPhase01) - if moonPhase01.Unix() != 1641148399 { - t.Fatal(moonPhase01) + if moonPhase01.Unix() != 1641148406 { + t.Fatal(moonPhase01.Unix()) } //离指定日期最近的朔月 moonPhase01 = ClosestShuoYue(date) fmt.Println("最近朔月", moonPhase01) - if moonPhase01.Unix() != 1643694349 { - t.Fatal(moonPhase01) + if moonPhase01.Unix() != 1643694356 { + t.Fatal(moonPhase01.Unix()) } //离指定日期最近的望月时间 moonPhase01 = ClosestWangYue(date) fmt.Println("最近望月", moonPhase01) - if moonPhase01.Unix() != 1642463294 { - t.Fatal(moonPhase01) + if moonPhase01.Unix() != 1642463301 { + t.Fatal(moonPhase01.Unix()) } //离指定日期最近的上弦月时间 moonPhase01 = ClosestShangXianYue(date) fmt.Println("最近上弦月", moonPhase01) - if moonPhase01.Unix() != 1641751864 { - t.Fatal(moonPhase01) + if moonPhase01.Unix() != 1641751871 { + t.Fatal(moonPhase01.Unix()) } //离指定日期最近的下弦月时间 moonPhase01 = ClosestXiaXianYue(date) fmt.Println("最近下弦月", moonPhase01) - if moonPhase01.Unix() != 1643118043 { - t.Fatal(moonPhase01) + if moonPhase01.Unix() != 1643118050 { + t.Fatal(moonPhase01.Unix()) } //------------------- for i := 0; i < 26; i++ { @@ -52,10 +52,3 @@ func Test_MoonPhaseDate(t *testing.T) { fmt.Println("上一朔月", moonPhase01) } } - -func TestMoon(t *testing.T) { - now := time.Now() - fmt.Println(RiseTime(now, 115, 40, 0, true)) - fmt.Println(CulminationTime(now, 115, 40)) - fmt.Println(DownTime(now, 115, 40, 0, true)) -} diff --git a/neptune/neptune_test.go b/neptune/neptune_test.go index 0039bd6..506b33c 100644 --- a/neptune/neptune_test.go +++ b/neptune/neptune_test.go @@ -7,8 +7,20 @@ import ( ) func TestNeptune(t *testing.T) { - date := time.Now().Add(time.Hour * -24) - fmt.Println(CulminationTime(date, 115)) - fmt.Println(RiseTime(date, 115, 23, 0, false)) - fmt.Println(DownTime(date, 115, 23, 0, false)) + tz := time.FixedZone("CST", 8*3600) + date := time.Date(2022, 01, 20, 00, 00, 00, 00, tz) + if NextConjunction(date).Unix() != 1647171796 { + t.Fatal(NextConjunction(date).Unix()) + } + if CulminationTime(date, 115).Unix() != 1642665021 { + t.Fatal(CulminationTime(date, 115).Unix()) + } + date, err := (RiseTime(date, 115, 40, 0, false)) + if err != nil { + t.Fatal(err) + } + if date.Unix() != 1642644398 { + t.Fatal(date.Unix()) + } + fmt.Println(DownTime(date, 115, 40, 0, false)) } diff --git a/sun/sun.go b/sun/sun.go index 222f02e..d3f95dd 100644 --- a/sun/sun.go +++ b/sun/sun.go @@ -2,7 +2,6 @@ package sun import ( "errors" - "math" "time" "b612.me/astro/basic" @@ -38,9 +37,11 @@ func RiseTime(date time.Time, lon, lat, height float64, aero bool) (time.Time, e if date.Hour() > 12 { date = date.Add(time.Hour * -12) } + //忽略时区的字面量时间 jde := basic.Date2JDE(date) _, loc := date.Zone() timezone := float64(loc) / 3600.0 + //risedate 时区修正后的时间,转换应当包括时区 riseJde := basic.GetSunRiseTime(jde, lon, lat, timezone, aeroFloat, height) if riseJde == -2 { err = ERR_SUN_NEVER_RISE @@ -80,7 +81,7 @@ func DownTime(date time.Time, lon, lat, height float64, aero bool) (time.Time, e } // MorningTwilight 晨朦影 -// date,当地时区日期 +// date,当地时区日期,返回的时间时区与此参数中的时区一致 // lon,经度,东正西负 // lat,纬度,北正南负 // angle,朦影角度:可选-6 -12 -18(民用、航海、天文) @@ -99,11 +100,11 @@ func MorningTwilight(date time.Time, lon, lat, angle float64) (time.Time, error) if calcJde == -1 { err = ERR_TWILIGHT_NOT_EXISTS } - return basic.JDE2Date(calcJde), err + return basic.JDE2DateByZone(calcJde, date.Location(), true), err } // EveningTwilight 昏朦影 -// date,当地时区日期 +// date,当地时区日期,返回的时间时区与此参数中的时区一致 // lon,经度,东正西负 // lat,纬度,北正南负 // angle,朦影角度:可选-6 -12 -18(民用、航海、天文) @@ -123,7 +124,7 @@ func EveningTwilight(date time.Time, lon, lat, angle float64) (time.Time, error) if calcJde == -1 { err = ERR_TWILIGHT_NOT_EXISTS } - return basic.JDE2Date(calcJde), err + return basic.JDE2DateByZone(calcJde, date.Location(), true), err } // EclipticObliquity 黄赤交角 @@ -256,10 +257,7 @@ func Zenith(date time.Time, lon, lat float64) float64 { // CulminationTime 太阳中天时间 // 返回给定经纬度、对应date时区date时刻的太阳中天日期 func CulminationTime(date time.Time, lon float64) time.Time { - jde := basic.Date2JDE(date) - if jde-math.Floor(jde) > 0.5 { - jde++ - } + jde := basic.Date2JDE(date.Add(time.Duration(-1*date.Hour())*time.Hour)) + 0.5 _, loc := date.Zone() timezone := float64(loc) / 3600.0 calcJde := basic.GetSunTZTime(jde, lon, timezone) - timezone/24.00 diff --git a/sun/sun_test.go b/sun/sun_test.go index 95b87e8..196e232 100644 --- a/sun/sun_test.go +++ b/sun/sun_test.go @@ -7,8 +7,23 @@ import ( ) func TestSun(t *testing.T) { - now := time.Now() - fmt.Println(RiseTime(now, 115, 40, 0, true)) + ja, err := time.LoadLocation("Asia/Tokyo") + if err != nil { + t.Fatal(err) + } + now, err := time.ParseInLocation("2006-01-02 15:04:05", "2020-01-01 00:00:00", ja) + if err != nil { + t.Fatal(err) + } + d, err := RiseTime(now, 115, 40, 0, true) + if err != nil { + t.Fatal(err) + } + if d.Format("2006-01-02 15:04:05") != "2020-01-01 08:41:45" { + t.Fatal(d.Format("2006-01-02 15:04:05")) + } fmt.Println(CulminationTime(now, 115)) fmt.Println(DownTime(now, 115, 40, 0, true)) + fmt.Println(MorningTwilight(now, 115, 40, -6)) + fmt.Println(EveningTwilight(now, 115, 40, -6)) }