🌸
【Go】春分の日と秋分の日をロジックで算出する
説明
以下にある通り、正式には前年の2月1日にならないと確定されないようです。
質問3-1)何年後かの春分の日・秋分の日はわかるの? | 国立天文台(NAOJ)
ただ、計算して予想することは可能みたいです。ここに書いてるロジックをGoで書きました。
春分・秋分の日 - 仕事に役立つエクセル実践問題集
実装
date.go
package equinox
import (
"math"
"time"
)
const (
JSTOffset = 9 * 60 * 60
asiaTokyo = "Asia/Tokyo"
)
var locationJST = time.FixedZone(asiaTokyo, JSTOffset)
// VernalEquinoxDate は春分の日を算出する
func VernalEquinoxDate(year int) time.Time {
return time.Date(year, time.March, calcVernalEquinoxDate(year), 0, 0, 0, 0, locationJST)
}
// AutumnalEquinoxDate は秋分の日を算出する
func AutumnalEquinoxDate(year int) time.Time {
return time.Date(year, time.September, calcAutumnalEquinoxDate(year), 0, 0, 0, 0, locationJST)
}
func calcVernalEquinoxDate(year int) int {
val := calcEquinoxBase(year)
switch {
case 1851 <= year && year <= 1899:
val += 19.8277
case 1900 <= year && year <= 1979:
val += 20.8357
case 1980 <= year && year <= 2099:
val += 20.8431
case 2100 <= year && year <= 2150:
val += 21.8510
}
return int(math.Floor(val))
}
func calcAutumnalEquinoxDate(year int) int {
val := calcEquinoxBase(year)
switch {
case 1851 <= year && year <= 1899:
val += 22.2588
case 1900 <= year && year <= 1979:
val += 23.2588
case 1980 <= year && year <= 2099:
val += 23.2488
case 2100 <= year && year <= 2150:
val += 24.2488
}
return int(math.Floor(val))
}
func calcEquinoxBase(year int) float64 {
return 0.242194*float64(year-1980) - math.Floor(float64(year-1980)/4.0)
}
テスト
date_test.go
package equinox
import (
"strconv"
"testing"
"time"
"github.com/stretchr/testify/assert"
)
func d(y, m, d int) time.Time {
return time.Date(y, time.Month(m), d, 0, 0, 0, 0, locationJST)
}
func TestVernalEquinoxDate(t *testing.T) {
tests := []struct {
year int
output time.Time
}{
{2015, d(2015, 3, 21)},
{2016, d(2016, 3, 20)},
{2017, d(2017, 3, 20)},
{2018, d(2018, 3, 21)},
{2019, d(2019, 3, 21)},
{2020, d(2020, 3, 20)},
{2021, d(2021, 3, 20)},
{2022, d(2022, 3, 21)},
{2023, d(2023, 3, 21)},
{2024, d(2024, 3, 20)},
{2025, d(2025, 3, 20)},
{2026, d(2026, 3, 20)},
{2027, d(2027, 3, 21)},
{2028, d(2028, 3, 20)},
{2029, d(2029, 3, 20)},
{2030, d(2030, 3, 20)},
}
for _, test := range tests {
t.Run(strconv.Itoa(test.year), func(t *testing.T) {
assert.Equal(t, test.output, VernalEquinoxDate(test.year))
})
}
}
func TestAutumnalEquinoxDate(t *testing.T) {
tests := []struct {
year int
output time.Time
}{
{2015, d(2015, 9, 23)},
{2016, d(2016, 9, 22)},
{2017, d(2017, 9, 23)},
{2018, d(2018, 9, 23)},
{2019, d(2019, 9, 23)},
{2020, d(2020, 9, 22)},
{2021, d(2021, 9, 23)},
{2022, d(2022, 9, 23)},
{2023, d(2023, 9, 23)},
{2024, d(2024, 9, 22)},
{2025, d(2025, 9, 23)},
{2026, d(2026, 9, 23)},
{2027, d(2027, 9, 23)},
{2028, d(2028, 9, 22)},
{2029, d(2029, 9, 23)},
{2030, d(2030, 9, 23)},
}
for _, test := range tests {
t.Run(strconv.Itoa(test.year), func(t *testing.T) {
assert.Equal(t, test.output, AutumnalEquinoxDate(test.year))
})
}
}
注意点
ロジックを見れば分かる通り、1850年以前と2151年以降には対応していません。1850年以前のシステムを開発する皆さんは十分気を付けましょう!
Discussion