Linux内核中的mktime()函数.
计算当前时间(>=1970-01-01 00:00:00) 距离1970-01-01 00:00:00的秒数
具体定义如下:
unsigned long
mktime(const unsigned int year0, const unsigned int mon0,
const unsigned int day, const unsigned int hour,
const unsigned int min, const unsigned int sec)
{
unsigned int mon = mon0, year = year0;
/* 1..12 -> 11,12,1..10 */
if (0 >= (int) (mon -= 2)) {
mon += 12; /* Puts Feb last since it has leap day */
year -= 1;
}
return ((((unsigned long)
(year/4 - year/100 + year/400 + 367*mon/12 + day) +
year*365 - 719499
)*24 + hour /* now have hours */
)*60 + min /* now have minutes */
)*60 + sec; /* finally seconds */
}
函数实现的表达式看出,实现mktime 最关键的是找出计算每月天数的方法。
当前时间距离0001-01-01 的天数:
1. 当前时间的月份mon>2, D(mon > 2) = year/4-year/100+year/400 + (year-1) *365 + month_to_days(mon) + day-1
2. 当前时间的月份mon <=2, D(mon <= 2) = (year-1)/4- (year-1)/100+ (year-1)/400 + (year-1)*365+ month_to_days(mon) + day-1
现在问题在于如何求出month_to_days(mon)表达式.
假定2月份是30天, mon 与 month_to_days(mon) 关系如下:
mon days month_to_days(mon) 数列标记{Dm}
1 31 0 D1 = 0
2 30 31 D2 = 31
3 31 61 D3 = 61
4 30 92 D4 = 92
5 31 122 D5 = 122
6 30 153 D6 = 153
7 31 183 D7 = 183
8 31 214 D8 = 214
9 30 245 D9 = 245
10 31 275 D10 = 275
11 30 306 D11 = 306
12 31 336 D12 = 336
讨论: m <= 7 情况
D2 - D1 = 31
D3 - D2 = 30
D4 - D3 = 31
D5 - D4 = 30
D6 - D5 = 31
D7 - D6 = 30
D8 - D7 = 31
上式相加, 可以得出: D2m+1 - D1 = 31*m+30*m = 61*m; D2m - D1 = 31*m+30*(m-1) = 61*m-30
由于D1 = 0. D2m+1 = 61*m ==> Dm = 61*(m-1)/2= (61*m-1)/2 - 30, m为<=7的奇数
D2m = 61*m-30 ==> Dm = 61*m/2 - 30 , m为<=7的偶数
讨论: m >7 情况
D9 - D8 = 31
D10 - D9 = 30
D11 - D10 = 31
D12 - D11 = 30
上式相加, 可以得出: D2m+1 - D8 = 31*(m-3)+30*(m-4) = 61*m-213; D2m - D8 = 31*(m-4)+30*(m-4) = 61*m-244
由于D8 = 214, D2m+1 = 61*m + 1 ==>Dm = 61*(m-1)/2 + 1 = (61*m+1)/2 - 30, m为>7奇数
D2m = 61*m - 30 ==> Dm = 61*m/2 - 30 , m 为>7偶数
综上所述: Dm = 61*m/2 - 30, m为偶数
Dm = (61*m-1)/2 - 30, m为<=7 奇数
Dm = (61*m+1)/2 - 30, m为>7 奇数
上述是在假定2月份为30天得出的结论, 如果考虑m>2月份的情况, 此时月份与天的数关系为:
Dm = 61*m/2 - 30 - 61 + 31 + 28 = 61*(m-2)/2 + 29, m为3 <= m <= 12的偶数 (1)
Dm = (61*m-1)/2 - 30 - 61 +31 + 28 = (61*(m-2)-1)/2 + 29, m为3 <= m <= 7 奇数 (2)
Dm = (61*m+1)/2 - 30 - 61 + 31 + 28 = (61*(m-2)+1)/2 + 29, m为8 <= m <= 12 奇数 (3)
(3)式:8 <= m <= 12 ==> 6 <= m-2 <= 10
==> 1 <= (m-2)/6 <= 10/6
==> 61*(m-2)+1 <= 61*(m-2)+ (m-2)/6 <= 61*(m-2) + 10/6
==> (61*(m-2)+1)/2 <= (61*(m-2)+ (m-2)/6)/2 <= (61*(m-2) + 10/6)/2 = (61*(m-2) + 1)/2 + 1/3
由于 m为奇数, 所以 (61*(m-2) + 1)/2 为整数, 按计算机取整 (61*(m-2) + 1)/2 = (61*(m-2) + 1)/2 + 1/3
即 (61*(m-2)+1)/2 = (61*(m-2)+ (m-2)/6)/2 = 367*(m-2)/12
Dm = 367*(m-2)/12 + 29
(2)式: 3<= m <= 7 ==> 1 <= m-2 <= 5
==> -1 < 1/6 <= (m-2)/6 <= 5/6 < 1
==> 61*(m-2)-1 < 61*(m-2) + (m-2)/6 < 61*(m-2) +1
==> (61*(m-2)-1)/2 < (61*(m-2) + (m-2)/6)/2 < (61*(m-2) +1)/2
由于 m为奇数, 所以 (61*(m-2)-1)/2 和 (61*(m-2) +1)/2 都为整数
而 (61*(m-2) +1)/2 -(61*(m-2)-1)/2 = 1
所以按计算机取整 (61*(m-2)-1)/2 = (61*(m-2) + (m-2)/6)/2 = 367*(m-2)/12
Dm = 367*(m-2)/12 + 29
(1)式: 3 <= m <= 12 ==> 1 <= m-2 <= 10
==> 0 < 1/12 <= (m-2)/12 <= 10/12 < 1
==> 61*(m-2)/2 < 61*(m-2)/2 + (m-2)/12 < 61*(m-2)/2 + 1
由m 为偶数, 所以 61*(m-2)/2 和 61*(m-2)/2 + 1 都为整数
而 61*(m-2)/2+1 - 61*(m-2)/2 = 1
所以按计算机取整 61*(m-2)/2 = 61*(m-2)/2 + (m-2)/12 = 367*(m-2)/12
Dm = 367*(m-2)/12 + 29
综上可以得出结论: m > 2 时, Dm = 367*(m-2)/12 + 29, 即 month_to_days(m) = 367*(m-2)/12 + 29
m = 1 时, Dm = 0, 即 month_to_days(1) = 0 = 367*(13-2)/12 + 29 - 365 = month_to_days(13) -365
m = 2时, Dm = 31, 即 month_to_days(2) = 31 = 367*(14-2)/12 + 29 - 365 = month_to_days(14) -365
即 m <= 2 时 month_to_days(m) = month_to_days(m+12) - 365
回到主问题:
当前时间距离0001-01-01 的天数:
1. 当前时间的月份mon>2, D(mon > 2) = year/4-year/100+year/400 + (year-1) *365 + month_to_days(mon) + day-1
2. 当前时间的月份mon <=2, D(mon <= 2) = (year-1)/4- (year-1)/100+ (year-1)/400 + (year-1)*365+ month_to_days(mon) + day-1
D(mon > 2) = year/4-year/100+year/400 + (year-1) *365 + month_to_days(mon) + day-1
= year/4-year/100+year/400 + (year-1) *365 + 367*(mon-2)/12 + 29+ day-1
= year/4-year/100+year/400 + year*365 + 367*(mon-2)/12 + day - 337
D(mon <= 2) = (year-1)/4- (year-1)/100+ (year-1)/400 + (year-1)*365+ month_to_days(mon) + day-1
= (year-1)/4- (year-1)/100+ (year-1)/400 + (year-1)*365 + month_to_days(mon+12) - 365 + day-1
= (year-1)/4- (year-1)/100+ (year-1)/400 + (year-1)*365 + 367*(mon+12-2)/12 + 29 - 365 + day-1
= (year-1)/4- (year-1)/100+ (year-1)/400 + (year-1)*365 + 367*(mon-2+12)/12 + day-337
D(1970-01-01 00:00:00) = 1969/4 - 1969/100 + 1969/400 + 1969*365 + 367*11/12 + 1 - 337 = 719162
D(mon
> 2) - D(1970-01-01
00:00:00) = (year/4-year/100+year/400
+ 367*(mon-2)/12 + day) + year*365-
719499
D(mon
<= 2) - D(1970-01-01
00:00:00) = ((year-1)/4- (year-1)/100+ (year-1)/400
+ 367*(mon-2+12)/12 + day) +
(year-1)*365 -719499
即为linux mktime 函数实现计算距离1970-01-01 00:00:00 天数的表达式