/* * linux/kernel/mktime.c * * (C) 1991 Linus Torvalds */ #include <time.h> /* * 这不是库函数,它仅供内核使用。因此我们不关心小于 1970 年的年份等,但假定一切均很正常。 * 同样,时间区域 TZ 问题也先忽略。我们只是尽可能简单地处理问题。最好能找到一些公开的库函数 * (尽管我认为 minix 的时间函数是公开的)。 * 另外,我恨那个设置 1970 年开始的人 - 难道他们就不能选择从一个闰年开始?我恨格里高利历、 * 罗马教皇、主教,我什么都不在乎。我是个脾气暴躁的人。 */ #define MINUTE 60 // 1 分钟的秒数。 #define HOUR (60*MINUTE) // 1 小时的秒数。 #define DAY (24*HOUR) // 1 天的秒数。 #define YEAR (365*DAY) // 1 年的秒数。 /* 有趣的是我们考虑进了闰年 */ // 下面以年为界限,定义了每个月开始时的秒数时间数组。 static int month[12] = { 0, DAY*(31), DAY*(31+29), DAY*(31+29+31), DAY*(31+29+31+30), DAY*(31+29+31+30+31), DAY*(31+29+31+30+31+30), DAY*(31+29+31+30+31+30+31), DAY*(31+29+31+30+31+30+31+31), DAY*(31+29+31+30+31+30+31+31+30), DAY*(31+29+31+30+31+30+31+31+30+31), DAY*(31+29+31+30+31+30+31+31+30+31+30) }; // 该函数计算从 1970 年 1 月 1 日 0 时起到开机当日经过的秒数,作为开机时间。 long kernel_mktime(struct tm * tm) { long res; int year; year = tm->tm_year - 70;// 从 70 年到现在经过的年数(2 位表示方式),因此会有2000年问题。 /* 为了获得正确的闰年数,这里需要这样一个魔幻偏值(y+1).*/ res = YEAR*year + DAY*((year+1)/4); // 这些年经过的秒数时间+每个闰年时多1天的秒数时间,在加上当年到当月时的秒数。 res += month[tm->tm_mon]; /* 以及(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 年以来经过的秒数时间。}
主要就是要分析这一句:
res = YEAR * year + DAY * ((year + 1) / 4); // 这些年经过的秒数时间 + 每个闰年时多1 天
是对闰年的处理。
从代码来看,对于当年是闰年,并不在第一个红色语句处对多出的一天进行累计,而是通过当年月份来增加这一天的秒数(在月份的数组中,从第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年到现在的闰年个数(如果今年是闰年则不计算进去)
/
if (tm->tm_mon>1 && ((year+2)%4)) res -= DAY;就简单多了:tm->tm_mon > 1表示,今年的月份已经超过3月了(所以2月份的天数就要被计算),(year + 2) % 4 表示,如果余数为0表示今年是闰年,那么就不需要减,如果余数不为0说明今年不是闰年,但是月份又超过3月了,所以在加月份的天数时,我们在2月里多加了1天,所以必须减掉。
linux0.11版内核的mktime分析
最新推荐文章于 2024-10-13 21:29:35 发布