linux0.11版内核的mktime分析

/*
 *  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天,所以必须减掉。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值