在Unix系统中有一条有趣的命令:cal,它是用来打印月历的(calendar)如图:
它还可以用来打印最近几个月的月历,甚至指定特定年份的全年月历。具体用法可以参见 man cal 或者 cal -help 命令执行结果。
那么问题来了,怎样使用程序打印一张月历?我们不妨观察一下月历的规律。
如上图所示:蓝圈内,2017年4月1号是星期六。2018年4月1号就是星期天。红圈内,2017年1月1号是星期天。2018年1月1号就是星期一。
以此规律类推,2019年1月1日是星期二,2020年1月1日是星期三。但是2016年1月1号是不是星期六呢?答案是否定的。
因为2016年是闰年,所以2016年2月有29天。这样从2016年2月28日后每个月的星期数都要向后推一天。这样导致了2017年1月1日变成星期日。
反过来说,2016年1月1日就是从2017年1月1日的星期日往前推两天。那么2016年1月1日就是星期五,而不是没有涉及闰月的星期六。
按照这个规律我们只要保存任意一个年份的任意一天是星期几,其他年份的日期星期数全部可以由该日期向前或者向后通过特定的迭代公式推得。
且慢,如果真要使用这种办法,假设我保存了2000年1月1日的星期数,我要推算2998年7月25日是星期几,运算量可想而知。
有没有知道“任意一个有效日期是星期几”的办法呢?通过搜索大量资料,这里我引入“蔡勒公式(Zeller's congruence )”
蔡勒公式如下:
图中:
h 是星期数(0=周六、1=周日、2=周一……6=周五)
q 是日。
m 是月份。
K 是当前年份的世纪。(年份 mod 100)
J 是当前年份除以100再向下取整后的值。
还需要注意的是,在蔡勒公式中一月和二月要分别看作上一年的13月和14月。比如:2017年1月等于2016年13月、2017年14月等于2018年2月。
用这个公式我们马上可以写出程序: