控制台月历
【题目】给定年份,月份,在控制台输出该月的月历
比如,2019年9月的月历:
很明显,这个问题的要点是:对给定的年、月,需要知道这个月有多少天,还需要知道它的1号是星期几。
可以参考以前关于日期问题的算法。
----判断某个年份是否为闰年
b闰年 :: Int -> Bool
b闰年 year | year `mod` 400 == 0 = True
| year `mod` 100 == 0 = False
| year `mod` 4 == 0 = True
| otherwise = False
----从公元1900年1月开始,逐月的天数无限表
monthDays :: [Int]
monthDays = concat [ [31,f x,31,30,31,30,31,31,30,31,30,31] | x<-[1900..] ]
where f x = if b闰年 x then 29 else 28
----从1900年1月1日 到 y年m月之前过去的天数
days :: Int -> Int -> Int
days y m = let n = (y-1900) * 12 + m - 1
in sum $ take n monthDays
---已知1900年1月1日是星期1,求之后的 y年 m月 1日是星期几
monthWeek :: Int -> Int -> Int
monthWeek y m = days y m `mod` 7
---单行按每行n个元素折成多行
foldLine :: Int -> [a] -> [[a]]
foldLine n xs
| length xs <= n = [xs]
| otherwise = take n xs : foldLine n (drop n xs)
---把一个串前边添空格,补到指定的长度为止
fillSpace :: Int -> String -> String
fillSpace n s
| length s >= n = s
| otherwise = ' ' : fillSpace (n-1) s
----已知 y年 m月,求当月的月历表
calendar :: Int -> Int -> [String]
calendar y m =
let passMon = (y-1900) * 12 + m - 1
thisMon = monthDays !! passMon
weekBegin = monthWeek y m
t = replicate weekBegin " " ++ map show [1..thisMon]
t2 = foldLine 7 t
t3 = map (map (fillSpace 2)) t2
t4 = map unwords t3
in "Mo Tu We Th Fr Sa Su" : t4
main :: IO ()
main = putStr $ unlines $ calendar 2019 9
这里算日期的基点取了 1900年1月1日,而不是公元元年。
星期的说法本就是后来的事。用今天的星期推公元元年星期是不准的。原因复杂。
我们现行的历法是:格里高利教皇历法。
1752年9月,英国议会决定把原来的恺撒历法改为现行的格里高利历法(200多年前就修定好了,却没人执行),因为这两个历法当前日期差11天,议会决定当年9月3日到到9月13日删去。也就是说9月2日后,直接是9月14日。
所以在日期转换,算星期等问题上,如果日期在1752年9月之前,要小心处理了。