/* Converts Gregorian date to seconds since 1970-01-01 00:00:00.
* Assumes input in normal date format, i.e. 1980-12-31 23:59:59
* => year=1980, mon=12, day=31, hour=23, min=59, sec=59.
*
* [For the Julian calendar (which was used in Russia before 1917,
* Britain & colonies before 1752, anywhere else before 1582,
* and is still in use by some communities) leave out the
* -year/100+year/400 terms, and add 10.]
*
* This algorithm was first published by Gauss (I think).
*
* WARNING: this function will overflow on 2106-02-07 06:28:16 on
* machines where long is 32-bit! (However, as time_t is signed, we
* will already get problems at other places on 2038-01-19 03:14:08)
*/
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 */
}
我们可以看出核心算式
Timestamp = (((Days * 24) + hour) * 60) + min) * 60 + sec
LeapDays = year/4 - year/100 + year/400
Days = year * 365 + 367* mon/12 + day - 719499 + LeapDays
其中LeapDays是用来计算到当前年份一共有多少个闰年,闰年规则是能被4整除,不能被100整除,世纪年能被400整除。
为了方便计算,我们将年份调整为从3月开始2月结束,这样做,我们可以将2月这个特殊月份,放到每年的最后一个月中。我们按照大小月规则,忽略2月份的特殊性,一年有367天。在给定的月和日下面,我们可以得到367 * mon / 12 ,就是指定的月在我们设定的规则下当年经过了多少天。
我们通过下面的代码来证明这个结论
#include<stdio.h>
#include<stdlib.h>
int main(){
int i;
unsigned long j = 0;
unsigned long n = 0;
unsigned long m = 0;
for(i = 1;i<=12;i++){
j = (unsigned long)( 367 * i / 12);
m = j - n;
n = j;
printf("月份:%d,从上月到达本月初经历了:%lu天\r\n",i+2,m);
}
return 0;
}
可以得出下面的结果:
月份:3,从上月到达本月初经历了:30天
月份:4,从上月到达本月初经历了:31天
月份:5,从上月到达本月初经历了:30天
月份:6,从上月到达本月初经历了:31天
月份:7,从上月到达本月初经历了:30天
月份:8,从上月到达本月初经历了:31天
月份:9,从上月到达本月初经历了:31天
月份:10,从上月到达本月初经历了:30天
月份:11,从上月到达本月初经历了:31天
月份:12,从上月到达本月初经历了:30天
月份:13,从上月到达本月初经历了:31天 //次年1月
月份:14,从上月到达本月初经历了:31天 //次年2月
当我们指定月份的时候,我们需要考虑的特殊月份只有3月份,所以我们得到了367*mon/12 - 30。同时当天也没有过完,就需要367*mon/12 + day -30 -1。
我们按照所有年份都是正常年份来算的话,每年有365天,那么当前年份没有过完,我们就可以知道到当前年份有 (year - 1) * 365。那么我们可以得到这么一个公式 (year -1) * 365 + 367 * mon/12 - 31 + day。
也就是相当于,我们忽略闰年影响,我们应当得到的天数总数为
(year -1 ) * 365 + 367 * mon/12 - 31 + 59 + day = year * 365 + 367 * mon/12 + day - 337
其中的59是由以下规则得出的,公元1年1月1日,到公元1年3月1日,一共有31 + 28 = 59天的差距。
从公元1年1月1日到1970年1月1日,一共经过了719162,所以我们可以得到 year * 365 + 367 * mon/12 + day -337 - 719162 = year * 365 + 367 * mon/12 + day- 719499。
这里面将3月份作为当年第一个月将2月份作为当年最后一个月,这个方式非常巧妙,简化了对特殊月份的处理。