作者 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]
********************************************************************/