2010年10月13日 星期三

Linux下time_t,struct tm,還有時間相關的一堆函式

搞不清楚Linux下面的time_t,struct tm,還有時間相關的一堆函式。

其實不是Linux的錯,是大家平常不那麼在乎時間的定義,或者說平常不在乎也沒關係。
時間要分成兩種,本地時間跟世界時間。
本地時間指的是當地的時間,像是台北時間,香港時間,美國時間,
世界各地方的本地時間是不同的。
如果你有看王建民的實況轉播的話,
你就會發現台灣現在半夜三點,可是美國那邊卻是出大太陽在下午三點。
或者是台灣現在已經早上8點了,美國那邊還是晚上8點。

本地時間參考的標準其實跟各地日出的時間有很大的關係。
各地的日出時間因為地球自轉的關係,有先後順序的差別,
拿台灣,英國,跟美國東岸來說,
台灣會先日出,過了8小時才輪到英國日出,再過4小時才輪到美國東岸日出。
因此我們說台灣時間比英國時間快了8小時,比美國時間快了4小時。
所以當人家說"你哪來的美國時間"阿,就是指你動作太慢了。
這也是為什麼每年跨年的時候,
台灣會先跨,然後到了隔天中午12點時,
新聞才會開始報導紐約時代廣場的跨年。
這裡可以參考一下鳥哥的文章http://linux.vbird.org/linux_server/0440ntp.php

而UTC(universal time, coordinated),"世界標準時間",指的就是世界時間,
或是也可以想成絕對時間。
各國的本地時間都可以轉換成世界時間,也就是UTC,
這樣世界各地的人就有一個統一的標準時間可以參考了。
這就像英語是全世界共通的語言,但各地仍可以用當地傳統的語言來溝通,
目的是相同的。
UTC其實就是英國法國的本地時間(因為世界世間是他們想出來的)
所以英國的世界時間用UTC來表示,本地時間也等於UTC。
而台灣的本地時間比英國的本地時間快了8小時,
所以台灣的世界時間用UTC+8表示,
(或是說台灣的日出時間比英國的日出時間快了8小時)
而紐約的世界時間則用UTC-4來表示。
(美國的日出時間比英國的日出時間慢了4小時)

回到Linux programming裡面。在Linux下面要看時間,可不是容易的事情。
在Linux下面要經過三個步驟
    1.程式設計師從電腦那邊取counter
    2.把counter轉換為本地或世界時間
    3.將時間顯示為一般使用者看的懂的時間
才能顯示出人類看的懂的時間。
counter是給電腦看的。程式設計師寫了一些函式把counter轉成程式設計師看的懂的,最後程式設計師再轉換成給一般人看的

[步驟一]
首先要知道在電腦內時間是利用counter來計算。
1970/1/1 00:00:00 counter為0。counter每秒鐘加1。
到2010年10月14日 2點59分41秒
(此處都是以UTC為準,此時台灣本地時間應該是2010年10月14日 10點59分41秒)
為止此counter的值為1287025181。
利用time()函式,我們可以取counter的值。
counter的表示就是用time_t來表示。

[步驟二]
取得counter的值後,接下來要作一個轉換。
轉換為程式設計師看的懂的格式(程式設計師也是人),
程式設計師用struct tm來表示時間。
struct tm {
    int tm_sec;         /* seconds */
    int tm_min;         /* minutes */
    int tm_hour;        /* hours */
    int tm_mday;        /* day of the month */
    int tm_mon;         /* month */
    int tm_year;        /* year */
    int tm_wday;        /* day of the week */
    int tm_yday;        /* day in the year */
    int tm_isdst;       /* daylight saving time */
};
有年月日時分秒,清楚多了。
Linux裡面提供了兩個函式可以把counter轉換為struct tm。
使用的函式是gmtime(),或是localtime()。
用gmtime()會把counter轉換為struct tm,轉換出來是UTC時間。
用localtime()跟用gmtime()很像,會把counter轉換為struct tm。
不同的是轉換出來的UTC時間,會再根據本機電腦時區的設定,再轉換為本地時間。

[步驟三]
轉換完之後接著才是顯示給一般的使用者來看。
也就是要把struct tm轉換給一般的使用者看。
顯示的時候用的函式是asctime(),參數是struct tm
這一連串的用法比較...怪,一個簡單的範例如下:
time_t times;
time(&times);
printf("time() is %ld\n",times);                                                          // time() is 1287025181
printf("asctime(GMT or UTC) is %s",asctime(gmtime(&times)));      // asctime(GMT or UTC) is Thu Oct 14 02:59:41 2010
printf("asctime(local) is %s",asctime(localtime(&times)));               // asctime(local) is Thu Oct 14 10:59:41 2010
這邊有一個函式可以簡化asctime(localtime),那就是ctime()。
範例如下 :
printf("asctime(local) is %s",asctime(localtime(&times)));               // ...Thu Oct 14 10:59:41 2010
printf("ctime equals to asctime(local), it is %s",ctime(&times));      // ...Thu Oct 14 10:59:41 2010
有沒有,雖然一個呼叫asctime(),一個呼叫ctime(),但是輸出卻是一模一樣。

如果程式設計師不喜歡asctime()顯示的方式時,可以改用strftime()這個函式。
範例如下:
char tbuf[64];
strftime(tbuf,sizeof(tbuf),"%Z:%Y/%m/%d %H:%M:%S",gmtime(&times));
printf("%s\n",tbuf);                                                                                             // GMT:2010/10/14 02:59:41
strftime(tbuf,sizeof(tbuf),"%Z:%Y/%m/%d %H:%M:%S",localtime(&times));
printf("%s\n",tbuf);                                                                                             // CST:2010/10/14 10:59:41

因此讓使用者看到目前時間的整體流程圖大約如下所示:
Machine - time() -> time_t -> gmtime() or localtime() -> struct tm -> asctime() or strftime() -> User

[reference]
http://zh.wikipedia.org/zh-tw/UTC%2B8
http://en.wikipedia.org/wiki/Coordinated_Universal_Time
http://linux.vbird.org/linux_server/0440ntp.php
http://linux.die.net/