每天進步一點點——Linux系統中的時間處理

來源:互聯網
上載者:User

轉載請說明出處:http://blog.csdn.net/cywosp/article/details/25839551


在程式中時間處理往往是一個麻煩的事,Linux系統提供了很多關於時間處理的函數,我們可以用這些函數來完成我們所需要的功能。那麼在程式中一般會關心哪些時間問題呢?
  • 真即時間:程式啟動並執行時間,即程式啟動到程式消亡所用時間或程式運行到現在所經過的時間
  • 進程時間:一個進程所使用的CPU時間總量,適用於對程式、演算法效能的檢查或最佳化
本文只關注真即時間的處理與轉換
一、Epoch無論地理位置如何,Linux系統內部對時間的表示方式均是以自Epoch以來的秒數來度量的,Epoch即通用協調時間(UTC,以前也稱為格林威治標準時間,或GMT)的1970年1月1日零點零分零秒。這大致也是UNIX系統問世的時間。該時間可儲存於time_t類型的變數中。
在32位Linux系統中,time_t是一個有符號整數,可以表示的日期範圍從1901年12月13日20時45分52秒至2038年1月19日03:14:07。因此,32位系統的機器都將面臨2038年的問題,當然這我們不必擔心,到了2038年,我相信這些系統早已升級為64位或者更多位的系統了。不過有些壽命更長的嵌入式裝置,可能還會受到此問題的影響。

二、時間轉換函式如所示,儲存於time_t變數的數值可以和其他時間格式相互轉換,其中還包含列印輸出。這些函數屏蔽了因時區、夏令時(DST)制和本地化等問題給轉換所帶來的種種複雜          

三、函數詳解    1. 系統調用gettimeofday(),用於tv指向的緩衝中返回日曆時間,其聲明如下
#include <sys/time.h>
// Return 0 on success, or -1 on errorint gettimeofday (struct timeval* tv, struct timezone* tz);
tv所指向的結構體如下struct timeval {    time_t          tv_sec;          /* Seconds since 00:00:00, 1 Jan 1970 UTC*/    susecond_t   tv_usec;        /* Additional microseconds (long int)*/};gettimeofday()函數提供到微秒級的精度並儲存於tv_usec中,這樣的精度對大多數程式來說已經能夠滿足。在x86-64平台上,gettimeofday()不是系統調用,而是在使用者態實現的,沒有環境切換和陷入核心的開銷,因此其速度是比較快的[1]。參數tz是一個曆史產物,在早期的UNIX系統實現中使用其來擷取系統的時區資訊,目前已被廢棄,因此在調用時始終置為NULL。

    2. 系統調用time(),其返回自Epoch以來的秒數,聲明如下
#include <time.h>
// Returns number of seconds since the Epoch, or (time_t)-1 on errortime_t time (time_t* timep);
如果timep參數不為NULL,那麼還會將自Epoch以來的的秒數置於timep所指向的位置中。但是在日常使用中我們都採用簡單的調用方式t = time (NULL)。從函數說明上可以得知gettimeofday()的精確度要比time()要高。

    3.  time_t可用於儲存Epoch到現在的秒數,這個數值非常不便於我們直接解釋,因為我們更容易接受的是MM-DD-YY HH:MIN:SS的形式。Linux中提供了簡單的ctime()函數來處理time_t的轉換
#include <time.h>
// Returns pointer to statically allocated string terminated by newline and \0 on success, or NULL on errorchar* ctime (const time_t* timep);
把一個指向time_t的指標作為timep參數傳入函數,將返回一個長達26個位元組的字串,內含標準格式的日期和時間,如下:    Wed May  14  15:22:34  2014該字串包含分行符號合終止空位元組各一。ctime()函數在進行行轉換時會自動對本地時區和DST設定加以考慮。返回的字串經由靜態分配的,因此無需調用free函數,正因如此,該函數是不可重新進入的(非安全執行緒的),安全執行緒的版為ctime_r()。

    4. time_t與struct tm之間的轉換
struct tm {    int tm_sec;      /*Seconds (0-60)*/    int tm_min;     /*Minites (0-59)*/    int tm_hour;   /*Hours (0-23)*/    int tm_mday;  /*Day of the month (1-31)*/    int tm_mon;   /*Month (1-12)*/    int tm_year;    /*Year since 1900*/    int tm_wday;   /*Day of the week (Sunday = 0)*/    int tm_yday;    /*Day in the year (0-365; 1 Jan = 0)*/    int tm_isdst;    /*Daylight saving time flag > 0: DST is in effect; = 0: DST is not effect; < 0: DST information not available*/
};結構體tm將日期和時間分解成多個獨立的欄位,這樣能方便程式擷取不同的欄位值來處理。欄位tm_sec的上限為60而不是59,這樣的設計主要是考慮閏秒,偶爾用其將人類日曆調整至精確的天文年(所謂的迴歸年)。如果程式中定義了_BSD_SOURCE測試宏,那麼有glibc定義的tm結構還會包括兩個欄位,一個為long int tm_gmtoff,用於表示時間超出UTC以東的秒數,一個為const char* tm_zone,用於表示時區的縮寫(例如:CEST為歐洲中部夏令時間)。
gmtime()和localtime()兩個函數可將time_t轉換成struct tm。gmtime()直接將time_t分解成UTC時間的tm,localtime()需要考慮時區和夏令時的設定,具體聲明如下:#include <time.h>
// Both return a pointer to a statically allocated broker-down time structure on success, or NULL on errorstruct tm* gmtime (const time_t *timep);struct tm* localtime (const time_t *timep);以上兩個函數都是非安全執行緒的,安全執行緒版本為gmtime_r()和localtime_r()
mktime()函數可以將struct tm轉換成time_t,其聲明如下:#include <time.h>
// Returns seconds since the Epoch corresponding to timeptr on success, or (time_t)-1 on errortime_t mktime (struct tm *timeptr);該函數可能會修改timeptr對應的值,至少會確保對tm_wday和tm_yday欄位的設定,確保這些欄位與其他欄位能夠相互對應起來。同時,mktime()在進行轉換時會對時區進行設定。此外,DST設定的使用與否取決於輸入欄位tm_isdst的值。
  • 若tm_isdst為0,則將這一時間視為標準時間(即,忽略夏令時)
  • 若tm_isdst大於0,則將這一時間視為夏令時
  • 若tm_isdst小於0,則試圖判定DST在每年的這一時間是否生效。這往往是眾望所歸的設定
    5. 將struct tm轉化易於理解的字串
#include <time.h>
// Returns pointer to statically allocated string terminated by newline and \0 on success, or NULL on errorchar* asctime (const struct tm *timeptr);
與ctime()相比,本地時區的設定對asctime()沒有影響。其返回的指標所指向的是由靜態分配的字串,因此其不是安全執行緒的,安全執行緒的版本為asctime_r(),輸出結果大概如下:    Wed May  14  16:43:21 CET 2014
asctime()函數返回的是一個固定形式的字串,有時為了更易於理解,程式不想僅局限於這樣的字串,於是Linux中提供了strftime()函數,聲明如下:#include <time.h>
// Returns number of bytes placed in outstr (excluding terminating null bytes) on success, or 0 on errorsize_t strftime (char *outstr, size_t maxsize, const char *format, const struct tm *timeptr);
outstr中返回的字串按照format參數定義的格式做了格式化,maxsize則是ourstr的最大長度,如果成功則將格式化後的內容寫入outstr所指向的緩衝區中,然後返回字串的真實長度,含終止空位元組,如果真實長度超過了maxsize參數的大小,那麼返回0以表示出錯,且無法確定outstr的內容。參數format是一個字串,類似於printf()參數。具體值在此不做詳解,可以google。
strptime()函數與strftime()函數正好相反,其可以將包含日期和時間的字串轉換成struct tm,聲明如下:#define _XOPEN_SOURCE#include <time.h>
// Returns pointer to next unprocessed character in str on success, or NULL on errorchar* strptime (const char *str, const char *format, struct tm *timeptr);
函數strptime()按照format參數內容對由日期和時間組成的字串str加以解析,並將轉換後的數值儲存於timeptr所指的緩衝中,如果成功,返回指向str中下一個還未解析過的字元,如果所給的str無法和format相對應,則會解析失敗,返回NULL,以示錯誤。最後還需注意,strptime()函數不會設定tm中的tm_isdst欄位。

四、時區不同的國家使用不同的時區和夏時制,對於要輸入輸出時間的程式來說,必須對時區進行考慮,尤其是全球的分布式系統。出於時區資訊太多,Linux系統沒有直接將其編碼於程式或函數庫中,而是以標準格式儲存於檔案。這些檔案位於/usr/share/zoneinfo目錄裡。系統的本地時區則由檔案/etc/localtime定義,其通常是一個連結到/usr/share/zoneinfo下的檔案。
時區檔案格式記敘於tzfile(5)手冊頁,其建立可通過zic(8)(時區資訊編譯器,zone information compiler)工具來完成。zdump(8)命令可根據指定時區檔案中的時區來顯示目前時間
為啟動並執行程式指定一個時區,需要將TZ環境變數設定為一個冒號(:)和時區名稱組成的字串,其中時區名稱定義於/usr/share/zoneinfo中。時區的設定會影響到ctime()、localtime()、mktime()、strftime()等函數,為了擷取時區設定,這些函數都會調用tzset(3)對如下全域變數進行設定:
char *tzname[2];     /*Name of timezone and alternate (DST) timezone*/int daylight;            /*Nonzero if there is an alternate (DST) timezone*/long timezone;       /*Seconds difference between UTC and local standard time*/

五、總結多種系統調用允許我們擷取和設定系統的時間,以及一系列的庫函數能夠使我們完成各種時間標記法之間的轉換。Linux是非即時的多任務作業系統,如果我們在程式中想完全精確的計時和定時時無法做到的,因為當前任務可能會被CPU隨時切換出去,但是在日常的時間處理中,以上這些函數已經足夠滿足需求了。本文沒有涉及到所講述的函數用例,如有在程式中使用到這些函數,可以百度或者google,再者你可以查看https://github.com/ApusApp/Swift/tree/master/swift/base中關於時間處理的實現。

參考[1] http://lwn.net/Articles/446528

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.