netssfy

mktime.c是如何处理闰年的

kernel_mktime (struct tm *tm)
{
  long res;
  int year;

  year = tm->tm_year - 70; // 从70 年到现在经过的年数(2 位表示方式),
  // 因此会有2000 年问题。
  /* magic offsets (y+1) needed to get leapyears right. */
  /* 为了获得正确的闰年数,这里需要这样一个魔幻偏值(y+1) */
  res = YEAR * year + DAY * ((year + 1) / 4); // 这些年经过的秒数时间 + 每个闰年时多1 天
  res += month[tm->tm_mon]; // 的秒数时间,在加上当年到当月时的秒数。
  /* and (y+2) here. If it wasn't a leap-year, we have to adjust */
  /* 以及(y+2)。如果(y+2)不是闰年,那么我们就必须进行调整(减去一天的秒数时间)。 */
  if (tm->tm_mon > 1 && ((year + 2) % 4))
    res -= DAY;

  res += DAY * (tm->tm_mday - 1); // 再加上本月过去的天数的秒数时间。
  res += HOUR * tm->tm_hour; // 再加上当天过去的小时数的秒数时间。
  res += MINUTE * tm->tm_min; // 再加上1 小时内过去的分钟数的秒数时间。
  res += tm->tm_sec;  // 再加上1 分钟内已过的秒数。
  return res;   // 即等于从1970 年以来经过的秒数时间。
}

佩服linus的天才,感谢赵博的注释

这个函数的作用就是根据开机时间,生成一个从1970年1月1日0时到开机时间的秒数。

红色部分就是对闰年的处理。

从代码来看,对于当年是闰年,并不在第一个红色语句处对多出的一天进行累计,而是通过当年月份来增加这一天的秒数(在月份的数组中,从第2项开始,2月都是算作29天的)

举个简单的例子,72年是闰年,72-70 = 2, (2+1)/4 = 0,这就证明了,当年是闰年的话,并不在红色部分对多出的一天进行累计。

如果72年变为73年,73-70 = 3,(3+1)/4 = 1。2个例子似乎说明year+1是必须的,而且是正确的。

但是为什么是加1呢,我仔细想了想,这个70年这个值有关系,因为70年往后2年才是闰年,所以加了1可以保证当年是闰年不进行累计(因为2+1=3,3/4=0)

如果把70这个值改变下,可能更容易发现问题。假设把70换成71年,那么71年往后1年才是闰年,所以就要变成(year + 2)了

是+1还是+2,就看最初的那个年份离他后面那个闰年差几年,反正这2个值相加要=3(表示不把当年计算进来),如果=4(表示把当年计算进来)

再把基数年变为72年,那么他后面那个闰年就是他自己(可以这样理解,72年的2月份还没有过完),所以应该+3.

X - 70 = year <=> X - 69 = year + 1,如果X是闰年,那么他减去69(68是闰年)再整除4的结果肯定比他减去68再整除4的结果小1,这就等于把当年是闰年给排除在外了。既(year+1)/4的结果是从70年到现在的闰年个数(如果今年是闰年则不计算进去)

 

至于第2个红色句子就比较简单了,tm->tm_mon > 1表示,今年的月份已经超过3月了(所以2月份的天数就要被计算),(year + 2) % 4 表示,如果余数为0表示今年是闰年,那么就不需要减,如果余数不为0说明今年不是闰年,但是月份又超过3月了,所以在加月份的天数时,我们在2月里多加了1天,所以必须减掉他

PS:X - 68 = year + 2,如果是闰年,那么肯定余数为0,如果不是闰年,余数肯定不为0

posted on 2008-11-12 15:21  everblue  阅读(1975)  评论(1)    收藏  举报

导航