http://blog.csdn.net/bokee/article/details/5330791
http://blog.csdn.net/bokee/article/details/5330682
時間處理時實際項目中經常碰到的問題,這裡介紹最常用的時間處理函數。
首先介紹基本的時間概念。時間一般分為兩種,一種是本地時間(Local Time),一種是國際標準時間間(Coordinated Universal Time ,UTC),也就是傳說中的格林威治時間。本地時間與UTC時間之間的差即為時差,比如,北京時間(東八區)比UTC時間晚8個小時。
C運行庫中處理時間的函數主要是這四個:
[cpp]
view plaincopyprint?
- time_t time(
- time_t *timer);
time_t類型為32位或64位整型,具體類型由編譯系統決定。此函數用來獲得從1970年1月1日子夜(這個時刻在不同的CRT實現中可能會不一樣)到當前時刻以來所流逝的時間,以秒為單位。這個時間差叫做日曆時間(Calendar Time )。
這是當然讓我困惑的地方:這個特殊的時刻——1970年1月1日零時零分零秒——是指本地時間呢,還是UTC時間呢?我認為是本地時間,也就是各個時區自己的1970年1月1日零時零分零秒。可以設想這樣一種情況,如果全球24時區各有一台電腦,都依次在自己所在時區的本地時間1970年1月1日零時1分零秒調用time函數,那麼傳回值都是60。注意,這裡是依次調用(事實上是每隔1小時),而不是想象中的同時調用,這是因為相鄰時區的同一本地時間,總是相差1小時。
當然,time_t型的時間方便電腦處理,但普通使用者無法理解這種數字。所以我們通常需要將time_t型時間轉換成我們平常所見的年月日形式。CRT中為此定義了tm結構。
[cpp]
view plaincopyprint?
- struct tm {
- int tm_sec; /* seconds after the minute - [0,59] */
- int tm_min;
/* minutes after the hour - [0,59] */
- int tm_hour; /* hours since midnight - [0,23] */
- int tm_mday;
/* day of the month - [1,31] */
- int tm_mon; /* months since January - [0,11] */
- int tm_year;
/* years since 1900 */
- int tm_wday; /* days since Sunday - [0,6] */
- int tm_yday;
/* days since January 1 - [0,365] */
- int tm_isdst; /* daylight savings time flag */
- };
注釋中已詳細解釋了各個欄位的用法。顯然這個結構中的欄位對使用者更有意義。我們通常用localtime_s函數將time_t時間轉換為tm時間。
[cpp]
view plaincopyprint?
- errno_t localtime_s(
- struct tm* _tm,
- const time_t *time);
其中第二個參數為傳入的time_t時間,第一個參數為返回的tm時間。由函數名可看出,返回的tm時間表示的是本地時間。當然,我們有時候也需要獲得對應的UTC時間,這時我們需要gmtime函數。
[cpp]
view plaincopyprint?
- errno_t gmtime_s(
- struct tm* _tm,
- const time_t* time);
後面我們會看到兩者的區別。
我們知道了如何將time_t時間轉換為tm時間。同樣,我們會需要將tm表示的時間轉換為time_t時間。這時我們需要mktime函數。
[cpp]
view plaincopyprint?
- time_t mktime(
- struct tm *timeptr);
此函數返回從"特殊時刻"到參數表示的時刻之間流逝的日曆時間。另外還有個很好用的特性,就是它能修正傳進來的tm結構中各欄位的取值範圍。比如,如果你將tm.tm_mon設為1,tm.tm_day設為33,然後以其為參數調用mktime函數,此函數會將tm.tm_mon修正為2,tm.tm_day修正為2。具體用法參照MSDN。
我們來分析下面範例程式碼:
[cpp]
view plaincopyprint?
- #include <stdlib.h>
- #include <stdio.h>
- #include <time.h>
- int main()
- {
- struct tm tmLocal, tmUTC;
- time_t tNow;
- //Get current calendar time
- time(&tNow);
- printf("Time Now from time(): %llu/n", tNow);
- //Get current local time
- localtime_s(&tmLocal, &tNow);
- printf("Local Time(YYYY-MM-DD HH:MM:SS): %d-%d-%d %d:%d:%d/n", tmLocal.tm_year + 1900, tmLocal.tm_mon,
- tmLocal.tm_mday, tmLocal.tm_hour, tmLocal.tm_min, tmLocal.tm_sec);
- //Get UTC time corresponding to current local time, and tmLocal.tm_hour - tmUTC.tm_hour = 8
- gmtime_s(&tmUTC, &tNow);
- printf("UTC Time (YYYY-MM-DD HH:MM:SS): %d-%d-%d %d:%d:%d/n", tmUTC.tm_year + 1900, tmUTC.tm_mon,
- tmUTC.tm_mday, tmUTC.tm_hour, tmUTC.tm_min, tmUTC.tm_sec);
- //convert tmLocal to calendar time
- tNow = mktime(&tmLocal);
- printf("Time Now from mktime(): %llu/n", tNow);
- return EXIT_SUCCESS;
- }
輸出結果如下:
上面代碼中,11行time函數獲得從"特殊時刻"到當前時刻的日曆時間,如輸出結果中的第一行顯示的1267192581秒。
14行localtime_s函數將日曆時間轉換為本地tm時間,如輸出結果第二行。
18行gmtime_s函數將將日曆時間轉換為對應的UTC的tm時間,如輸出結果第三行顯示。很容易看出,第二,三行輸出的時間相差8小時,因為我在東八區。如果你修改自己電腦的時區(在控制台的Date and Time中修改),再運行此程式,比較兩次的運行結果,你就可以更好的理解了。
22行mktime函數將tm時間轉換為日曆時間,輸出結果中第四行顯示的結果與第一行一樣,這是必須的。。。
//-----------------------------------------------------------------------------------------------------------------------------------------
上文中介紹了C運行庫中的時間處理函數。這一篇介紹Windows SDk中提供的時間函數。兩種時間系統之間沒有本質區別(事實上CRT時間是用Windows時間實現的,當然這是說的VC實現),同樣提供本地時間和UTC時間之間的轉換。不過CRT中的tm時間在SDK中對應為系統時間(SYSTEMTIME),CRT中的time_t時間在SDK中對應的為檔案時間(FILETIME),那個"特殊時刻"也變成1601年1月1日的子夜。
當然,首先要弄清楚FILETIME與SYSTEMTIME定義。
[cpp]
view plaincopyprint?
- typedef struct _FILETIME {
- DWORD dwLowDateTime;
- DWORD dwHighDateTime;
- } FILETIME, *PFILETIME;
- typedef struct _SYSTEMTIME {
- WORD wYear;
- WORD wMonth;
- WORD wDayOfWeek;
- WORD wDay;
- WORD wHour;
- WORD wMinute;
- WORD wSecond;
- WORD wMilliseconds;
- } SYSTEMTIME, *PSYSTEMTIME;
比較一下,很明顯,FILETIME與time_t類似,是64位整型,不過FILETIME是以100納秒(ns)為單位。SYSTEMTIME與tm類似,不過多了一項wMilliseconds。可以看出,SDK時間比CRT的時間提供了更高的精度。同時SDK提供了更豐富的函數來處理時間。
[c-sharp]
view plaincopyprint?
- void GetSystemTime(
- LPSYSTEMTIME lpSystemTime);
- void GetLocalTime(
- LPSYSTEMTIME lpSystemTime);
這兩個函數獲得SYSTEMTIME形式的目前時間,不過GetSystemTime函數獲得當前的UTC時間,GetLocalTime獲得當前的本地時間,可以想象,獲得的兩個時間存在著時差。類似於CRT中提供tm與time_t之間的轉換,SDK也提供了兩個函數來轉換SYSTEMTIME時間與FILETIME時間。
[cpp]
view plaincopyprint?
- BOOL SystemTimeToFileTime(
- const SYSTEMTIME* lpSystemTime,
- LPFILETIME lpFileTime);
- BOOL FileTimeToSystemTime(
- const FILETIME* lpFileTime,
- LPSYSTEMTIME lpSystemTime);
函數命名很self-explanatory,就不用多說了吧。
SDK還提供了兩個很有趣的函數。
[cpp]
view plaincopyprint?
- BOOL LocalFileTimeToFileTime(
- const FILETIME* lpLocalFileTime,
- LPFILETIME lpFileTime);
- BOOL FileTimeToLocalFileTime(
- const FILETIME* lpFileTime,
- LPFILETIME lpLocalFileTime);
LocalFileTimeToFileTime函數將本地的FILETIME時間轉換為對應的UTC的FILETIME時間。我覺得,這個函數只是通過將本地時間減去與UTC時間的時間差來實現轉換,比如在東八區的本地時間轉換為對應的UTC時間,只需要將本地時間減去8*60*60*1000*1000*10(單位100ns)。類似,FileTimeToLocalFileTime函數是將UTC時間轉換為本地時間,它只是將減去時間差換成加上時間差。
瞭解了這些功能,讓我們用代碼說話吧。
[cpp]
view plaincopyprint?
- #include <stdlib.h>
- #include <stdio.h>
- #include <time.h>
- #include <windows.h>
- int main()
- {
- SYSTEMTIME stLocal, stUTC, stUTC2;
- FILETIME ftLocal, ftUTC, ft;
- ULARGE_INTEGER uli;
- GetLocalTime(&stLocal);
- GetSystemTime(&stUTC);
- printf("Local System Time(YYYY-MM-DD HH:MM:SS): %d-%d-%d %d:%d:%d/n", stLocal.wYear, stLocal.wMonth,
- stLocal.wDay, stLocal.wHour, stLocal.wMinute, stLocal.wSecond);
- printf("UTC System Time (YYYY-MM-DD HH:MM:SS): %d-%d-%d %d:%d:%d/n", stUTC.wYear, stUTC.wMonth,
- stUTC.wDay, stUTC.wHour, stUTC.wMinute, stUTC.wSecond);
- SystemTimeToFileTime(&stLocal, &ftLocal);
- uli.LowPart = ftLocal.dwLowDateTime;
- uli.HighPart = ftLocal.dwHighDateTime;
- printf("Local File Time: %llu/n", uli.QuadPart);
- LocalFileTimeToFileTime(&ftLocal, &ftUTC);
- uli.LowPart = ftUTC.dwLowDateTime;
- uli.HighPart = ftUTC.dwHighDateTime;
- printf("UTC File Time: %llu/n", uli.QuadPart);
- FileTimeToSystemTime(&ftUTC, &stUTC2);
- printf("UTC System Time2 (YYYY-MM-DD HH:MM:SS): %d-%d-%d %d:%d:%d/n", stUTC2.wYear, stUTC2.wMonth,
- stUTC2.wDay, stUTC2.wHour, stUTC2.wMinute, stUTC2.wSecond);
- return EXIT_SUCCESS;
- }
程式輸出結果如下:
代碼13行GetLocalTime函數獲得當前的本地SYSTEMTIME時間,14行獲得對應的UTC的SYSTEMTIME時間,如輸出結果前兩行所顯示,兩者相差8小時(淩晨還在寫部落格,表揚下自己。。。)。
20行SystemTimeToFileTime函數將本地SYSTEMTIME時間轉換為方便計算的本地FILETIME形式時間,如輸出結果第三行所顯示。
25行LocalFileTimeToFileTime函數將本地FileTime時間轉換為對應的UTC的FILETIME時間,如輸出結果第四行所顯示。就像前面介紹的,如果你將輸出結果第三,四兩行所顯示的數字相減,併除以10*1000*1000*60*60,你將會得出8,你可以算下試試,記住FILETIME是以100納秒為單位的。
最後30行FileTimeToSystemTime將FILETIME時間轉換為SYSTEMTIME時間。可以看出輸出結果中第五行與第二行相同,這是必須的,因為兩者都是當前本地時間對應的UTC時間。