linux中c++時間時區簡介

作者 mail: [email protected]

    c++中時間主要分爲GMT時間和本地時間。GMT時間叫做格林威治時間,也就是UTC時間,這個時間有點特殊,它所在時區爲0,在這個時區內,本地時間和GMT時間完全一致,地球上其他地方的時間都得在這個時間基礎上加一個時區,這纔是其他地方的本地時間。地球被分爲24個時區,相鄰時區相差一個小時。比如中國上海是+8區,意思就是,假如格林威治現在是27日1點0分0秒,而中國上海就是27日早上9點0分0秒。我們比格林威治早8個小時進入27號。具體的時區信息請看《 http://zh.wikipedia.org/wiki/時區列表 》,(時區從UTC-12到UTC+14)

    時區信息存放在/usr/share/zoneinfo/中,如上海時區文件就是/usr/share/zoneinfo/Asia/Shanghai中。時區可以由環境變量TZ設置,上海的就可以export TZ="Asia/Shanghai"。在c++程序中調用tzset()函數可以由環境變量TZ初始化時區信息。如果沒有環境變量TZ,tzset()還會使用/etc/localtime來初始化時區信息。TZ環境變量可以指向絕對路徑,也可以是相對路徑。如果是相對路徑則以/usr/share/zoneinfo/爲基準路徑。
    日光節約時間(daylight saving time; DST)又稱夏令時間或夏時制,由於夏季太陽昇起的時間比冬季早(特別是高緯度地區),人們爲了充分利用日照來節省照明用電,因此特地將時間提起一個小時讓民衆能夠早睡早期,這個制度叫做日光節約時間。世界上一百多個國家或地區實行,包含臺灣,香港,日本,美國,歐洲,但中國大陸好像沒有實行。在struct tm中有一項tm_isdst表示日光節約時間,當爲0時,表示沒有日光節約時間,當小於0時表示根據系統的時區信息來判斷是否執行日光節約時間。當大於0時,表示應該加的日光節約時間,單位爲秒。
    在c++中有一個時間點比較重要,那就是GMT的1970-01-01 00:00:00 +0000 (UTC)。這是在0時區的時間,即格林威治時間或UTC時間,這個時間學名叫做Epoch,新紀元。爲什麼這個時間比較特殊?因爲如果你在那個時刻調用time()函數,你會發現返回的值是0! 現在我們隨便去調一把,返回的都是10位數了。我剛纔調了一下是1309164941,是UTC時間的2011-06-27 08:55:41 +0000 (UTC),也是中國時間的2011-06-27 16:55:41 +7080 (CST)。7080是16進制數,等於10進制的28800秒,即8個小時。
    c++的時間中,有幾個數據結構比較常用,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 */
           };
           struct timeval {
               time_t      tv_sec;     /* seconds */
               suseconds_t tv_usec;    /* microseconds */
           };
           struct timezone {
               int tz_minuteswest;     /* minutes west of Greenwich */
               int tz_dsttime;         /* type of DST correction */
           };

    下面介紹c++中的幾個重要時間函數。
       time_t time(time_t *t);

    這大概能算是最常用的一個時間函數了,該函數返回從Epoch開始的秒數,如果t != NULL,則返回值填充到t所指的內存中。注意:返回的是UTC標準的秒數,如果你在格林威治和上海同時調用這個函數,則這2個值是一樣的,都是格林威治的當前秒數,並沒有算時區。

       int gettimeofday(struct timeval *tv, struct timezone *tz);
       int settimeofday(const struct timeval *tv, const struct timezone *tz);

    這個timeval是相對time_t更精確的時間,包含了微妙數。tv_usec最大爲999999,再加1則爲1秒,超過1秒就進位到tv_sec。timezone一般傳入NULL,linux沒有處理timezone中的tz_dsttime信息。

       struct tm *gmtime(const time_t *timep);
       struct tm *gmtime_r(const time_t *timep, struct tm *result);

    這兩個函數意思一樣,將time_t這個秒數轉換成以UTC時區爲標準的年月日時分秒時間。gmtime_r是線程安全的,推薦使用這個。gmtime返回的是一個struct tm*,這個指針指向一個靜態的內存,這塊區域是會經常被改動的。你剛調用gmtime(),執行了其他幾條命令,然後想使用剛纔gmtime()得到struct tm,會發現內容不對了,所以很危險,我就被搞得一頭霧水,再gdb了多次之後,發現是這麼個情況,改用gmtime_r後就沒有問題,gmtime_r會將結果保存到你傳入的內存中。

       struct tm *localtime(const time_t *timep);
       struct tm *localtime_r(const time_t *timep, struct tm *result);

    這兩個函數意思也一樣,會根據時區信息得到本地時間,在上海同時調用localtime_r 和gmtime_r會發現,localtime_r得到小時數會多8,因爲我們是+8區。同樣建議使用localtime_r版本。

       time_t mktime(struct tm *tm);

    將已經根據時區信息計算好的struct tm轉換成time_t的秒數。計算出的秒數是以UTC時間爲標準的,跟調用time()得到的秒數是同一個概念。這個struct tm是本地時間,如:上海時間2011-08-20 13:20:40,struct tm中各個值就是此時間的對應值,因爲這個時間中已經包含了時區信息。如果在格林威治和上海對同一個struct tm調用這個函數,返回的值是不一樣的,上海的值會比格林威治少8個小時。

    再介紹一下本地時間和UTC時間之間的轉換。本地時間是在UTC時間基礎上根據時區和日光節約時間調整得到。<time.h>中如下函數和變量起到了轉換本地時間和UTC時間的作用。
       void tzset (void);
       extern char *tzname[2];
       extern long timezone;
       extern int daylight;

    具體代碼:
    #include <time.h>
    tzset ();
    time_t current_timet;
    time(¤t_timet);//得到當前時間秒數
    struct tm utc_tm;
    gmtime_r (¤t_timet, &utc_tm);//得到GMT,即UTC時間
    struct tm local_tm;
    localtime_r(¤t_timet, &local_tm);//得到本地時間,根據如下打印看出兩者相差8小時。
    printf ("current_timet: %d, timezone info:[%d / %d]\n", current_timet, timezone, daylight);
    printf ("utc_tm: %d-%d-%d %d:%d:%dZ\n", utc_tm.tm_year, utc_tm.tm_mon, utc_tm.tm_mday, utc_tm.tm_hour, utc_tm.tm_min, utc_tm.tm_sec);
    printf ("local_tm: %d-%d-%d %d:%d:%d\n", local_tm.tm_year, local_tm.tm_mon, local_tm.tm_mday, local_tm.tm_hour, local_tm.tm_min, local_tm.tm_sec);
    struct tm local_tm2 = utc_tm;
    local_tm2.tm_sec -= timezone;
    local_tm2.tm_isdst = daylight; //將utc時間轉換成本地時間
    time_t local_timet2 = mktime(&local_tm2);//得到秒數
    printf ("\nlocal_timet2=%d\n", local_timet2);//可以看出,和一開始得到的當前時間秒數相同。daylight部分沒有仔細測。


    痛苦的回顧,我曾經寫過如下代碼
time_t now;
time(&now);
DEBUG ("now:%d",now)
struct tm* timeNow = gmtime (&now);
    DEBUG ("timeNOw:sec=%d, min=%d, hour=%d, mday=%d, mon=%d, year=%d, wday=%d, yday=%d, isdst=%d",
           timeNow->tm_sec, timeNow->tm_min, timeNow->tm_hour, timeNow->tm_mday, timeNow->tm_mon,
           timeNow->tm_year, timeNow->tm_wday, timeNow->tm_yday, timeNow->tm_isdst);
struct tm tm1 = *timeNow;
    DEBUG ("tm1    :sec=%d, min=%d, hour=%d, mday=%d, mon=%d, year=%d, wday=%d, yday=%d, isdst=%d",
           tm1.tm_sec, tm1.tm_min, tm1.tm_hour, tm1.tm_mday, tm1.tm_mon,
           tm1.tm_year, tm1.tm_wday, tm1.tm_yday, tm1.tm_isdst);

結果,打印出來的出乎意料。我認爲timeNow和tm1的內容應該是一樣的,可是tm_hour竟然相差8。後來發現DEBUG()中有時間處理的,使得靜態內存被修改了。但類似DEBUG()這種函數的問題誰會很關注。所以,時間方面最好使用可重入版本。

/*******************************************************************
• 此文章解釋權歸windsome所有,如要轉載無須聯繫本人。
• QQ: 35327864
• msn: [email protected]
• mail: [email protected]
 ********************************************************************/



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章