zoukankan      html  css  js  c++  java
  • 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);

    在游戏的实际应用中,一般都是根据具体需求来显示格式化的时间,甚至要做一些边界时间的特殊处理,因此这里都不详细讨论时间的格式化显示了。

  • 相关阅读:
    css3文字单位rem 设置文字大小
    JS实现多物体width缓冲运动实例
    vs 你不得不会的调试方式
    C# 常用修饰符
    富文本编辑器tinymce
    Swagger简单实例
    marquee标签详解
    table数据跑马灯效果
    SqlServer发布订阅
    ORM概述及常用ORM框架
  • 原文地址:https://www.cnblogs.com/kevonyang/p/5928942.html
Copyright © 2011-2022 走看看