C++中的時間函數


C++獲取時間函數眾多,何時該用什么函數,拿到的是什么時間?該怎么用?很多人都會混淆。

本文是本人經歷了幾款游戲客戶端和服務器開發后,對游戲中時間獲取的一點總結。

 

最早學習游戲客戶端時,為了獲取最精確的時間,使用兩個函數

BOOL QueryPerformanceFrequency(LARGE_INTEGER *lpFrequency);

BOOL QueryPerformanceCounter(LARGE_INTEGER *lpCount);

這兩個函數分別是獲取CPU的時鍾頻率和CPU計數器,是能夠獲取到的最精確的時間差。對於需要獲取每幀走過的精確時間,使用這兩個函數是最最精確的。

#include<windows.h>

LARGE_INTEGER lFrequency;
QueryPerformanceFrequency(&lFrequency);

LARGE_INTEGER lBeginCount;
QueryPerformanceCounter(&lBeginCount);

Sleep(100);

LARGE_INTEGER lEndCount;
QueryPerformanceCounter(&lEndCount);

double time = (double)(lEndCount.QuadPart - lBeginCount.QuadPart) / (double)lFrequency.QuadPart;

在客戶端代碼的時間處理模塊中,每一幀調用QueryPerformanceCounter獲取當前的counter,即可獲取每一幀使用的時間。(當然現在有Unity,估計沒人關注這倆函數了)

 

雖然利用這兩個函數能夠精確的統計經過的時間,但是卻無法得到當前時間,並且以上兩個函數是Windows系統所特有的,unix/linux系統中並不具備。

為了獲取系統的當前精確時間,需要使用另一個系統函數

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

獲取從1970年1月1日到現在經過的時間和時區(UTC時間),(按照linux的官方文檔,時區已經不再使用,正常應該傳NULL)。

#include <sys/time.h>

struct timeval start_tv;

gettimeofday(&start_tv, NULL);

sleep(1000);

struct timeval end_tv;

gettimeofday(&end_tv, NULL);

double time = (end_tv.tv_sec - start_tv.tv_sec) + (double)(end_tv.tv_usec - start_tv.tv_usec)/(double)1000000;

這樣同樣可以獲得精確到微秒的每幀經過的時間。服務器上的幀運行機制,一般便是這個時間函數來計算和同步。

 

如果不需要非常精確的時間,而只要精確到秒,可以使用另一個時間函數

time_t time(time_t* timer);

該函數返回一個UTC時間戳,如果傳入timer參數,則為timer設置時間戳的值。

 

然而以上兩個函數獲取的都是UTC時間戳,如果在游戲中需要顯示當前時區的時間,該怎么辦呢?

使用localtime或localtime_r,兩者效果一致,只是獲取結果參數位置不同。

struct tm *localtime(const time_t *timep); // 傳入UTC時間戳,返回當前時區的tm結構指針

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

第一個函數獲取tm結構靜態變量的指針,第二個函數則傳入一個tm結構變量的地址,並為之賦值。最終得到的tm變量,存儲了當前時區的時間。

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 */
};

 通常來說服務器和客戶端通信同步時間時,不會傳這么多int,只會傳一個int64的UTC時間戳,給客戶端自己轉成當前時區。另外服務器也不會每次都調一次localtime轉一次。一般來說,服務器和客戶端都是維護一個int64的當前時間的UTC時間戳,以及一個當前時區的偏移時間,(客戶端還會定時更新和服務器的時間誤差,對時)。

因此,需要用到另外兩個函數:

struct tm *gmtime(const time_t *timep); // time_t 到 tm 的轉換,前后都是UTC時間,沒有時區轉換

struct tm *gmtime_r(const time_t *timep, struct tm *result); // gmtime 傳入result方式

time_t mktime(struct tm *timeptr); // localtime的逆向操作,從當前時區的 tm 轉到UTC的 time_t

在進程啟動時

time_t t0 = 0; // 當前時間為0時

time_t t1 = mktime(gmtime(&t0)); // UTC的時間

int timezone_diff = (int)(t0 - t1); // 當前時區和UTC的時間差

每次需要拿到當前時區時間,只需要用 UTCStamp + timezone_diff 即可。

除了獲取時間,為了格式化顯示時間,我們也可以利用一些系統函數,格式化輸出時間字符串

char *asctime(const struct tm *tm);

char *asctime_r(const struct tm *tm, char *buf);

char ctime(const time_t *timep);

char ctime_r(const time_t *timep, char *buf);

在游戲的實際應用中,一般都是根據具體需求來顯示格式化的時間,甚至要做一些邊界時間的特殊處理,因此這里都不詳細討論時間的格式化顯示了。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM