简单分析Tomohiko Sakamoto的dayofweek代码

来源:互联网 发布:汉倭奴国王金印 知乎 编辑:程序博客网 时间:2024/06/05 08:52

[C FAQ]20.27 由一个日期, 怎样知道是星期几?

用mktime() 或localtime() (参见问题13.11 和13.12, 如果tm hour 的值位0,要注意DST (夏时制) 的调整); 或者Zeller 的congruence (参阅sci.math FAQ); 或者这个由Tomohiko Sakamoto 提供的优雅的代码:

int dayofweek(int y, int m, int d) /* 0 = Sunday */{    static int t[] = {0, 3, 2, 5, 0, 3, 5, 1, 4, 6, 2, 4};    y -= m < 3;    return (y + y/4 - y/100 + y/400 + t[m-1] + d) % 7;}

参见问题13.12 和20.28。
参考资料: [ISO, Sec. 7.12.2.3]。

简单分析:

  1. 公元一年一月一日为星期一(现在世界各国通用一星期七天的制度。这个制度最早由君士坦丁大帝[Constantine the Great]制定。他在公元321年3月7日正式宣布7天为1周,这个制度一直沿用至今)。
  2. 算今天到公元一年一月一日有多少天,%7,一周7天,周而复始。
  3. 每年365天,365=52*7+1,所以,过一年,在算星期的时候,就相当于多了一天。
  4. 闰年多一天。过一个闰年,在3月及以后就要多加一天。
  5. 公元一年各月一日的星期:t0[] = {1, 4, 4, 0, 2, 5, 0, 3, 6, 1, 4, 6}。
  6. 本质:日期+月修正+年修正+闰年修正,模7,得到星期几。
  7. 我们看上面的常数组t[],其实就是将t0对应的1、2月-1,其后的-2。这个-2,因为是相对公元一年一月一日,所以y-1、m-1和d-1,其中m-1体现在下标中,y和d合起来就是-2了。前两个月-1,是因为在算闰年的时候,将1、2月的y多减了1,在t中补上。即,t[]不仅仅是月份修正常数,而是一个年月日的综合修正常数
  8. 以上这个代码,用常数组隐藏了一些算法细节,使得代码变得相当的帅。