与时间相关的操作在开发中是十分常见的,本文总结了常用时间接口,并给出了示例代码。

1 获取当前系统的时间

在C++中用于表示时间的数据结构有: time_tstruct tmstruct timevalstruct timespec 等。

1.1 获取 time_t 格式的时间

std::time_t time(std::time_t* arg);

其中 arg 参数可以是现有变量的地址,结果就会存储在该变量中;当然, arg 参数也可以是 nullptr

1.2 获取 struct tm 格式的时间

struct tm 提供了详细的,便于阅读的时间格式。可以通过这个时间格式直接获得常用的时间信息,如月份、星期等。

struct tm {
    int tm_sec;
    int tm_min;
    int tm_hour;
    int tm_mday;
    int tm_mon;
    int tm_year;
    int tm_wday;
    int tm_yday;
    int tm_isdst;
};

一般通过将 time_t 转换为 struct tm 的方式获取该格式的时间。

std::tm* localtime(const std::time_t* time);

std::tm* localtime_s(const std::time_t* time, struct tm* result);

在多线程情况下,推荐使用线程安全的版本 localtime_s ,这个版本使用了用户指定的变量地址用来存储结果。

1.3 获取 struct timeval 格式的时间

timeval 格式能够表示精确到微秒(us)的时间。

typedef struct timeval {
    long tv_sec;
    long tv_usec;
} timeval;

在linux系统中,使用 gettimeofday 获取 struct timeval 格式的当前时间。

int gettimeofday(struct tm* tp, void* tzp);

在windows系统中,没有 gettimeofday 的官方实现,可以使用以下代码。

#define WIN32_LEAN_AND_MEAN
#include <Windows.h>
#include <stdint.h> // portable: uint64_t   MSVC: __int64

int gettimeofday(struct timeval * tp, struct timezone * tzp) {
    // Note: some broken versions only have 8 trailing zero's, the correct epoch has 9 trailing zero's
    // This magic number is the number of 100 nanosecond intervals since January 1, 1601 (UTC)
    // until 00:00:00 January 1, 1970
    static const uint64_t EPOCH = ((uint64_t) 116444736000000000ULL);

    SYSTEMTIME  system_time;
    FILETIME    file_time;
    uint64_t    time;

    GetSystemTime( &system_time );
    SystemTimeToFileTime( &system_time, &file_time );
    time =  ((uint64_t)file_time.dwLowDateTime )      ;
    time += ((uint64_t)file_time.dwHighDateTime) << 32;

    tp->tv_sec  = (long) ((time - EPOCH) / 10000000L);
    tp->tv_usec = (long) (system_time.wMilliseconds * 1000);
    return 0;
}

(以上代码摘自stackoverflow)

1.4 获取 struct timespec 格式的时间

timespec 数据结构从C++17 / C11开始支持, timespec 提供精确到纳秒(ns)的时间表示方法。

struct timespec {
    time_t   tv_sec;        /* seconds */
    long     tv_nsec;       /* nanoseconds */
};

使用 timespec_get 函数获取 timespec ,该函数也是 C++17 / C11 中的函数接口。

int timespec_get(struct timespec* ts, int base);

2 时间形式的转换

2.1 std::time_tstd::tm ( struct tm )转换

std::time_t 转换为 std::tm 的函数:

std::tm* localtime(const std::time_t* time);

// (C11接口,线程安全)
struct tm* localtime_s(const time_t* restrict time,
		       struct tm* restrict result);

std::tm* gmtime(const std::time_t* time);

// (C11接口,线程安全)
struct tm* gmtime_s(const time_t* restrict time,
		    struct tm* restrict result);

std::tm 转换为 std::time_t 的函数:

time_t mktime(struct tm *time);

2.2 std::time_t 与字符串转换

std::time_t 转换为字符串的函数:

char* ctime(const time_t* time);

// (C11接口,线程安全)
errno_t ctime_s(const time_t* time, char* buf);

2.3 std::tm 与字符串转换

std::tm 转换为字符串的函数:

char* asctime(const struct tm* time_ptr);

errno_t asctime_s(char *buf, rsize_t bufsz,
		  const struct tm *time_ptr);

std::size_t strftime(char* str, std::size_t count,
		     const char* format, const std::tm* time);

// C++11
template< class CharT >
/*unspecified*/ put_time(const std::tm* tmb, const CharT* fmt);

3 计算时间间隔

推荐使用 std::chrono::steady_clock 进行时间间隔的计算,因为 steady_clock 是单调的,不会受到校时的影响。不推荐使用 std::chrono::system_clock 进行时间间隔的计算,由于它会受到系统时间更改的影响,如果在计时间隔中修改了系统时间,则本次的计算结果会收到影响。

如果编译器不支持C++11,可以考虑以系统启动时间作为基准进行时间间隔的计算。

4 C++11风格的时钟接口

C++11中提供了 std::chrono 库,丰富了与时间相关的函数,其中定义了三种主要的类型:

  • clock 时钟相关
  • time point 某个时间点
  • duration 一段时间

更详细的资料可以参考 cppreference