春分日、秋分日

春分日、秋分日を計算する方法が紹介されていましたので計算してみました。

{-
-- 春分日、秋分日 http://www.is.akita-u.ac.jp/~sig/holidays.html
1851 - 1899	[19.2811 + 0.242194 * (y - 1980) - [(y - 1983) / 4]]
1900 - 1979	[20.8357 + 0.242194 * (y - 1980) - [(y - 1983) / 4]]
1980 - 2099	[20.8431 + 0.242194 * (y - 1980) - [(y - 1980) / 4]]
2100 - 2150	[21.8510 + 0.242194 * (y - 1980) - [(y - 1980) / 4]]

1851 - 1899	[22.2588 + 0.242194 * (y - 1980) - [(y - 1983) / 4]]
1900 - 1979	[23.2588 + 0.242194 * (y - 1980) - [(y - 1983) / 4]]
1980 - 2099	[23.2488 + 0.242194 * (y - 1980) - [(y - 1980) / 4]]
2100 - 2150	[24.2488 + 0.242194 * (y - 1980) - [(y - 1980) / 4]]
[x] は, xの小数点以下切り捨てを意味する
-}
-- 西暦を引数とする春分の日を返す関数
vernalEquinoxDay :: Int -> Maybe Int
vernalEquinoxDay   y | y >= 1851 && y <= 1899 = Just $ calc y 19.2811 1983
                     | y >= 1900 && y <= 1979 = Just $ calc y 20.8357 1983
                     | y >= 1980 && y <= 2099 = Just $ calc y 20.8431 1980
                     | y >= 2100 && y <= 2150 = Just $ calc y 21.8510 1980
                     | otherwise              = Nothing

-- 西暦を引数とする秋分の日を返す関数
autumnalEquinoxDay :: Int -> Maybe Int
autumnalEquinoxDay y | y >= 1851 && y <= 1899 = Just $ calc y 22.2588 1983
                     | y >= 1900 && y <= 1979 = Just $ calc y 23.2588 1983
                     | y >= 1980 && y <= 2099 = Just $ calc y 23.2488 1980
                     | y >= 2100 && y <= 2150 = Just $ calc y 24.2488 1980
                     | otherwise              = Nothing
-- 演算部分
calc :: Int -> Double -> Int -> Int
calc y n1 n2 = truncate (n1 + 0.242194 * realToFrac (y - 1980) - realToFrac (truncate (fromIntegral(y - n2) / 4)))

-- (最初が年、次が春分の日、秋分の日)のタプルのリストを返す
> map (\x->(x,vernalEquinoxDay x, autumnalEquinoxDay x)) [1851..2150]
[(1851,Just 21,Just 24),(1852,Just 20,Just 23),(1853,Just 20,Just 23),(1854,Just 20,Just 23),(1855,Just 21,Just 23),
  (略)
 (2010,Just 21,Just 23),(2011,Just 21,Just 23),(2012,Just 20,Just 22),(2013,Just 20,Just 23),(2014,Just 21,Just 23),
 (2015,Just 21,Just 23),(2016,Just 20,Just 22),(2017,Just 20,Just 23),(2018,Just 21,Just 23),(2019,Just 21,Just 23),
 (2020,Just 20,Just 22),(2021,Just 20,Just 23),(2022,Just 21,Just 23),(2023,Just 21,Just 23),(2024,Just 20,Just 22),
  (略)
 (2146,Just 21,Just 23),(2147,Just 21,Just 23),(2148,Just 20,Just 22),(2149,Just 20,Just 23),(2150,Just 21,Just 23)]

春分日から秋分日までは186日、秋分日から春分日までは179日・・・等間隔かと思っていた・・・Orz

Prelude>  :m + Data.Time.Calendar

Prelude Data.Time.Calendar>  diffDays  (fromGregorian 2011 9 23) (fromGregorian 2011 3 21)
-- => 186

Prelude Data.Time.Calendar>  diffDays (fromGregorian 2012 3 20) (fromGregorian 2011 9 23) 
-- => 179

Prelude Data.Time.Calendar> 186 + 179
-- => 365
-- 2011 夏至
> truncate (22.2776 + 0.241669 * realToFrac (2011 - 1900) - realToFrac (truncate (fromIntegral(2011 - 1900) / 4)))
-- => 22

-- 2011 冬至
> truncate (22.6587 + 0.242752 * realToFrac (2011 - 1900) - realToFrac (truncate (fromIntegral(2011 - 1900) / 4)))
-- => 22

-- 2011 春分日 --> 2011 夏至
>  diffDays  (fromGregorian 2011 6 22)  (fromGregorian 2011 3 21)
-- =>93

-- 2011 夏至 --> 2011 秋分日
>  diffDays  (fromGregorian 2011 9 23) (fromGregorian 2011 6 22)
-- =>93


-- 2011 秋分日 2011 冬至
>  diffDays  (fromGregorian 2011 12 22) (fromGregorian 2011 9 23) 
-- =>90

-- 2011 冬至 --> 2011 春分日
>  diffDays  (fromGregorian 2012 3 20)  (fromGregorian 2011 12 22) 
-- =>89

> 93+93+90+89
365


【参考】
春分日・秋分日を算出する数字の意味。

夏至冬至なども計算できそう。