日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > linux >内容正文

linux

Linux mktime 源代码简析

發布時間:2025/3/21 linux 24 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Linux mktime 源代码简析 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

這里選擇從另外一個角度再次解析這部分代碼,建議先閱讀上面的博客內容:

?

?

?
  • /* 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 */

  • }

  • ?

    這個函數的功能是獲得1970年1月1日至今的秒數,我設這個數為totol_sec

    首先需要獲得公元元年1月1日至今的天數,我們設這個數為days

    ?

    顯然:

    ?

    totol_sec = ((days * 24 + hour) * 60 + min) * 60 + sec

    后面的部分比較簡單,難點在于獲取days

    設leapdays為公元0年到今天之前(不包含今天)的閏天數
    設ydays為今年1月1日至今的天數
    另: 公元0年到1970年1月1日的天數為719162天

    因此:

    ?

    days = leapdays + (year0 - 1) * 365 + ydays - 719162 (1)


    為了更好的理解上面的源代碼,我們引入變量mon, year, magic,其中mon和year都是源碼中用到的變量

    ?

    ?
  • 當mon0 > 2 時, mon = mon0 - 2, year = year0 (2a)

  • 當mon0 <= 2 時, mon = mon0 + 10, year = year0 - 1 (2b)

  • magic = 367 * mon / 12

  • 上面的(2a) (2b)等價于源碼中的

    ?

    ?
  • /* 1..12 -> 11,12,1..10 */

  • if (0 >= (int) (mon -= 2)) {

  • mon += 12; /* Puts Feb last since it has leap day */

  • year -= 1;

  • }

  • ?

    遍歷mon0(參見上面的博客內容)我們可以發現magic有如下的性質

    ?

    ?
  • 當mon0 > 2 時, ydays = magic + day + 28 (3a)

  • 當mon0 <= 2 時, ydays = magic + day - 365 + 28 (3b)

  • 同時,無論mon0為何值,year是否等于year0都有

    ?

    leapdays = year/4 - year/100 + year/400 (4)


    結合(1)(2a)(3a)(4), 當mon0 > 2 時

    ?

    ?
  • days = year/4 - year/100 + year/400 + (year - 1) * 365

  • + 367 * mon / 12 + day + 28 - 719162

  • ?

    結合(1)(2b)(3b)(4), 當mon0 <= 2 時

    ?

    ?
  • days = year/4 - year/100 + year/400 + year * 365

  • + 367 * mon / 12 + day - 365 + 28 - 719162


  • 得到相同的等式:

    ?

    ?
  • days = (year/4 - year/100 + year/400 + 367*mon/12 + day)

  • + year*365 - 719499

  • 這也就是源碼中獲取經過天數的公式。

    ?

    可以看到源碼中最精彩最難以理解的部分就是

    ?

    367*mon/12

    我們再來看看這個算式的計算結果:

    ?

    ?
  • 計算值

  • mon mon0 value

  • 1 (3) 30

  • 2 (4) 61

  • 3 (5) 91

  • 4 (6) 122

  • 5 (7) 152

  • 6 (8) 183

  • 7 (9) 214

  • 8 (10) 244

  • 9 (11) 275

  • 10 (12) 305

  • 11 (1) 336

  • 12 (2) 367


  • 可以發現這個計算結果實際上隱含了一個信息就是,它恰好代表著之前月份天數的和month_day_in_year ,只是這個和是一個經過偏轉的值即

    ?

    value = month_day_in_year - 29

    1月和2月比較特殊,因為他們被加上了12個月,也就是1年,他們的value實際上應該先減去365,也就是

    ?

    value - 365 = month_day_in_year - 29


    也就是說367 * mon / 12這個算式經過取整運算剛好能得出月份天數和的信息,這應該是一個數學上的巧合。

    據說是高斯最先提出這種算法,實在是佩服這些天才。

    我也參看了glibc相同功能的代碼(參見glicb源碼time/mktime.c),在glibc中是通過查表法來獲得月份天數信息的

    ?

    ?
  • const unsigned short int __mon_yday[2][13] =

  • {

  • /* Normal years. */

  • { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 },

  • /* Leap years. */

  • { 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366 }

  • };


  • 因為linux源碼中已經處理了閏年的情況,所以我們只需要關注Normal years的部分就行了。根據上面的分析倒算回去對比一下,其實是一樣的。

    從效率上來說,linux和glibc的mktime都是差不多的,但是linux版本的mktime不需要依靠全局變量。

    總結

    以上是生活随笔為你收集整理的Linux mktime 源代码简析的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。