1. 程式人生 > >Windows中的時間(SYSTEMTIME和FILETIME)&CRT中的時間(time_t和tm)

Windows中的時間(SYSTEMTIME和FILETIME)&CRT中的時間(time_t和tm)

時間處理時實際專案中經常碰到的問題,這裡介紹最常用的時間處理函式。

首先介紹基本的時間概念。時間一般分為兩種,一種是本地時間(Local Time),一種是協調世界時間(Coordinated Universal Time ,UTC),也就是傳說中的格林威治時間。本地時間與UTC時間之間的差即為時差,比如,北京時間(東八區)比UTC時間晚8個小時。

C執行庫中處理時間的函式主要是這四個:

[cpp] view plaincopyprint?
  1. time_t time(
  2. 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?
  1. structtm {
  2. int tm_sec; /* seconds after the minute - [0,59] */
  3. int tm_min; /* minutes after the hour - [0,59] */
  4. int tm_hour; /* hours since midnight - [0,23] */
  5. int tm_mday; /* day of the month - [1,31] */
  6. int tm_mon; /* months since January - [0,11] */
  7. int tm_year; /* years since 1900 */
  8. int tm_wday; /* days since Sunday - [0,6] */
  9. int tm_yday; /* days since January 1 - [0,365] */
  10. int tm_isdst; /* daylight savings time flag */
  11. };

註釋中已詳細解釋了各個欄位的用法。顯然這個結構中的欄位對使用者更有意義。我們通常用localtime_s函式將time_t時間轉換為tm時間。

[cpp] view plaincopyprint?
  1. errno_t localtime_s(
  2. structtm* _tm,
  3. consttime_t *time);

其中第二個引數為傳入的time_t時間,第一個引數為返回的tm時間。由函式名可看出,返回的tm時間表示的是本地時間。當然,我們有時候也需要獲得對應的UTC時間,這時我們需要gmtime函式。

[cpp] view plaincopyprint?
  1. errno_t gmtime_s(
  2. structtm* _tm,
  3. consttime_t* time);

後面我們會看到兩者的區別。

我們知道了如何將time_t時間轉換為tm時間。同樣,我們會需要將tm表示的時間轉換為time_t時間。這時我們需要mktime函式。

[cpp] view plaincopyprint?
  1. time_t mktime(
  2. structtm *timeptr);

此函式返回從"特殊時刻"到引數表示的時刻之間流逝的日曆時間。另外還有個很好用的特性,就是它能修正傳進來的tm結構中各欄位的取值範圍。比如,如果你將tm.tm_mon設為1,tm.tm_day設為33,然後以其為引數呼叫mktime函式,此函式會將tm.tm_mon修正為2,tm.tm_day修正為2。具體用法參照MSDN。

我們來分析下面示例程式碼:

[cpp] view plaincopyprint?
  1. #include <stdlib.h>
  2. #include <stdio.h>
  3. #include <time.h>
  4. int main()
  5. {
  6. structtm tmLocal, tmUTC;
  7. time_t tNow;
  8. //Get current calendar time
  9. time(&tNow);
  10. printf("Time Now from time(): %llu/n", tNow);
  11. //Get current local time
  12. localtime_s(&tmLocal, &tNow);
  13. printf("Local Time(YYYY-MM-DD HH:MM:SS): %d-%d-%d %d:%d:%d/n", tmLocal.tm_year + 1900, tmLocal.tm_mon,
  14. tmLocal.tm_mday, tmLocal.tm_hour, tmLocal.tm_min, tmLocal.tm_sec);
  15. //Get UTC time corresponding to current local time, and tmLocal.tm_hour - tmUTC.tm_hour = 8
  16. gmtime_s(&tmUTC, &tNow);
  17. printf("UTC Time (YYYY-MM-DD HH:MM:SS): %d-%d-%d %d:%d:%d/n", tmUTC.tm_year + 1900, tmUTC.tm_mon,
  18. tmUTC.tm_mday, tmUTC.tm_hour, tmUTC.tm_min, tmUTC.tm_sec);
  19. //convert tmLocal to calendar time
  20. tNow = mktime(&tmLocal);
  21. printf("Time Now from mktime(): %llu/n", tNow);
  22. return EXIT_SUCCESS;
  23. }

輸出結果如下:

輸出結果

上面程式碼中,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?
  1. typedefstruct _FILETIME {
  2. DWORD dwLowDateTime;
  3. DWORD dwHighDateTime;
  4. } FILETIME, *PFILETIME;
  5. typedefstruct _SYSTEMTIME {
  6. WORD wYear;
  7. WORD wMonth;
  8. WORD wDayOfWeek;
  9. WORD wDay;
  10. WORD wHour;
  11. WORD wMinute;
  12. WORD wSecond;
  13. WORD wMilliseconds;
  14. } SYSTEMTIME, *PSYSTEMTIME;

比較一下,很明顯,FILETIME與time_t類似,是64位整型,不過FILETIME是以100納秒(ns)為單位。SYSTEMTIME與tm類似,不過多了一項wMilliseconds。可以看出,SDK時間比CRT的時間提供了更高的精度。同時SDK提供了更豐富的函式來處理時間。

[c-sharp] view plaincopyprint?
  1. void GetSystemTime(
  2. LPSYSTEMTIME lpSystemTime);
  3. void GetLocalTime(
  4. LPSYSTEMTIME lpSystemTime);

這兩個函式獲得SYSTEMTIME形式的當前時間,不過GetSystemTime函式獲得當前的UTC時間,GetLocalTime獲得當前的本地時間,可以想象,獲得的兩個時間存在著時差。類似於CRT中提供tm與time_t之間的轉換,SDK也提供了兩個函式來轉換SYSTEMTIME時間與FILETIME時間。

[cpp] view plaincopyprint?
  1. BOOL SystemTimeToFileTime(
  2. const SYSTEMTIME* lpSystemTime,
  3. LPFILETIME lpFileTime);
  4. BOOL FileTimeToSystemTime(
  5. const FILETIME* lpFileTime,
  6. LPSYSTEMTIME lpSystemTime);

函式命名很self-explanatory,就不用多說了吧。

SDK還提供了兩個很有趣的函式。

[cpp] view plaincopyprint?
  1. BOOL LocalFileTimeToFileTime(
  2. const FILETIME* lpLocalFileTime,
  3. LPFILETIME lpFileTime);
  4. BOOL FileTimeToLocalFileTime(
  5. const FILETIME* lpFileTime,
  6. LPFILETIME lpLocalFileTime);

LocalFileTimeToFileTime函式將本地的FILETIME時間轉換為對應的UTC的FILETIME時間。我覺得,這個函式只是通過將本地時間減去與UTC時間的時間差來實現轉換,比如在東八區的本地時間轉換為對應的UTC時間,只需要將本地時間減去8*60*60*1000*1000*10(單位100ns)。類似,FileTimeToLocalFileTime函式是將UTC時間轉換為本地時間,它只是將減去時間差換成加上時間差。

瞭解了這些功能,讓我們用程式碼說話吧。

[cpp] view plaincopyprint?
  1. #include <stdlib.h>
  2. #include <stdio.h>
  3. #include <time.h>
  4. #include <windows.h>
  5. int main()
  6. {
  7. SYSTEMTIME stLocal, stUTC, stUTC2;
  8. FILETIME ftLocal, ftUTC, ft;
  9. ULARGE_INTEGER uli;
  10. GetLocalTime(&stLocal);
  11. GetSystemTime(&stUTC);
  12. printf("Local System Time(YYYY-MM-DD HH:MM:SS): %d-%d-%d %d:%d:%d/n", stLocal.wYear, stLocal.wMonth,
  13. stLocal.wDay, stLocal.wHour, stLocal.wMinute, stLocal.wSecond);
  14. printf("UTC System Time (YYYY-MM-DD HH:MM:SS): %d-%d-%d %d:%d:%d/n", stUTC.wYear, stUTC.wMonth,
  15. stUTC.wDay, stUTC.wHour, stUTC.wMinute, stUTC.wSecond);
  16. SystemTimeToFileTime(&stLocal, &ftLocal);
  17. uli.LowPart = ftLocal.dwLowDateTime;
  18. uli.HighPart = ftLocal.dwHighDateTime;
  19. printf("Local File Time: %llu/n", uli.QuadPart);
  20. LocalFileTimeToFileTime(&ftLocal, &ftUTC);
  21. uli.LowPart = ftUTC.dwLowDateTime;
  22. uli.HighPart = ftUTC.dwHighDateTime;
  23. printf("UTC File Time: %llu/n", uli.QuadPart);
  24. FileTimeToSystemTime(&ftUTC, &stUTC2);
  25. printf("UTC System Time2 (YYYY-MM-DD HH:MM:SS): %d-%d-%d %d:%d:%d/n", stUTC2.wYear, stUTC2.wMonth,
  26. stUTC2.wDay, stUTC2.wHour, stUTC2.wMinute, stUTC2.wSecond);
  27. return EXIT_SUCCESS;
  28. }

程式輸出結果如下:

程式輸出結果

程式碼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時間。