最近项目中遇到了些关于时间处理的问题,恰好想系统的学习下,所以有了这篇文章
基础知识
关于时间相关的基础知识,我之前有写过一篇文章,请移步 >>>这里<<<。
相关类型
clock_t
clock_t
头文件:<time.h>
| <ctime>
定义:
typedef /* unspecified */ clock_t;
这里的 unspecified
随系统版本和编译器而定,我这里给出 64位Windows 10系统
下 VisualStudio2017 编译器 x86编译选项
下的定义:
typedef long clock_t;
time_t
time_t
头文件:<time.h>
| <ctime>
定义:
typedef /* unspecified */ clock_t;
这里的 unspecified
随系统版本和编译器而定,我这里给出 64位Windows 10系统
下 VisualStudio 2017 编译器 x86编译选项
下的定义:
typedef __int64 __time64_t;
typedef __time64_t time_t;
尽管没有被C标准定义,但是这几乎总是一个整数值,它保存自1970年1月1日0时0分0秒
以来的秒数(不包括闰秒),对应于posix时间,专业术语对他的称呼为日历时间
。而1970年1月1日0时0分0秒
又被称为Epoch
。
struct tm
struct tm
头文件:<time.h>
| <ctime>
定义:
struct tm
{
int tm_sec; // seconds after the minute - [0, 60](since C99/C++11) including leap second。
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
};
这里的定义应该在各个平台系统下都一样,不过值得注意的是版本,tm_sec在C99/C++11
之前的取值范围是 [0,61],但是在同一分钟内两个闰秒是不允许的,所以在C99/C++11
之后被修复。
如果夏令时生效,则夏令时标志为正,如果是非夏令时,则为0,如果此信息不可用,则为负数。
而且还有一点要注意:tm_year表示的是距1900年的差值,所以如果想表示2000年的话 tm_year是100。
struct timespec
struct timespec
头文件:<time.h>
| <ctime>
定义:
struct timespec
{
time_t tv_sec; // Seconds - >= 0
long tv_nsec; // Nanoseconds - [0, 999999999]
};
这个结构包含秒和纳秒,当然,实际上硬件是不一定支持这个精确度的,它自从C11/C++17
之后被定义。
struct timeval
struct timeval
头文件:<sys/time.h>
定义:
struct timeval{
time_t tv_sec; /*秒s*/
suseconds_t tv_usec; /*微秒us*/
};
此结构体定义在Unix/Linux系统
的sys/time.h头文件
中,事实上更加正确的说法是BSD的time.h头文件。不过,Windows中timeval结构由select函数在windows Socket中使用,以指定函数可以完成的最大时间。时间间隔是tv_sec和tv_usec成员中值的组合。
获取和设置时间函数
clock
clock
头文件:<time.h>
定义:
clock_t clock(void);
这个函数返回从“开启这个程序进程”到“程序中调用clock()函数”时之间的CPU时钟计时单元(clock tick)数。如果该信息不可用或其值不能表示,则返回(clock_t)(-1)
。
在time.h文件中,还定义了一个常量CLOCKS_PER_SEC
,它用来表示一秒钟会有多少个时钟计时单元,其定义如下:
#define CLOCKS_PER_SEC ((clock_t)1000)
所以可以通过除以 CLOCKS_PER_SEC
来转换为秒。
只有时钟不同调用的两个返回值之间的差值才是有意义的,因为时钟的开始时间不一定与程序的开始一致。
时钟时间可能比壁挂时钟提前或延迟,这取决于操作系统给予程序的执行资源。例如,如果cpu被其他进程共享,则时钟时间可能会比挂钟慢。另一方面,如果当前进程是多线程的并且多于一个执行核心可用,则时钟时间可能比壁钟提前得更快。
在POSIX兼容的系统上,使用clock_gettime()
并且设置clock id 为CLOCK_PROCESS_CPUTIME_ID
是一种更好的解决方案。
在某些系统的实现中,clock
的返回值可能会被模上一个数,例如,32位 clock_t 每2147秒或者是36分钟被模一次。
time
time
头文件:<time.h>
定义:
time_t time( time_t *arg );
返回类型为time_t对象的当前日历时间,并将其存储在由arg指向的time_t对象中(除非arg是空指针)。
而time_t之前有介绍过,其值代表的是自1970年1月1日0时0分0秒
以来的秒数(不包括闰秒)。
所以time
函数有两个地方可以得到日历时间,一个是作为参数传入的arg 一个是返回值。
当返回值为(time_t)(-1)
时代表发生错误。
time_t中日历时间的类型是未指定的,但是大多数系统符合posix规范,并且返回一个整数类型的值,它保存了自1970年1月1日0时0分0秒
以来的秒数。其中time_t是32位有符号整数(许多历史实现)的实现将会在2018年溢出。
timespec_get
timespec_get
头文件:<time.h>| <ctime>
定义:
int timespec_get( struct timespec *ts, int base) //(since C11)
int timespec_get( std::timespec* ts, int base) //(since C++17)
#define TIME_UTC /* implementation-defined */ //(since C11/C++17)
这里的 unspecified
随系统版本和编译器而定,我这里给出 64位Windows 10系统
下 VisualStudio 2017 编译器 x86编译选项
下的定义:
#define TIME_UTC 1
此函数修改ts
指向的timespec
对象,以保持指定时间基础上的当前日历时间。
用作timespec_get
的基本参数的值由TIME_
开始的其他宏常量可以由实现来提供以指示附加的时间基础
例如base
是TIME_UTC
,那么:
ts-> tv_sec
被设置为自实现定义时期以来的秒数,截断为整数值
ts-> tv_nsec
成员被设置为整数纳秒,四舍五入到系统时钟的精度
POSIX函数clock_gettime(CLOCK_REALTIME, ts)
也可以用从epoch开始的时间来填充一个std :: timespec
。
如果调用成功的话,返回值为基于基础时间的值,否则的话就是0。
clock_getres
clock_getres
clock_gettime
clock_gettime
clock_settime
clock_settime
头文件:<sys/time.h> & <unistd.h>
//Unix/Linux
定义:
int clock_getres(clockid_t clock_id, struct timespec *res);
int clock_gettime(clockid_t clock_id, struct timespec *tp);
int clock_settime(clockid_t clock_id, const struct timespec *tp);
clock_getres函数是用来获取对应时钟类型能够提供的时间精确度,res参数保存其精确度。在设置的时候,设置的时间值也应该是这个精确度的倍数。如果res参数为空指针,则不返回时钟精度。
函数clock_gettime是用来获取对应时钟的时间。
函数clock_settime则是用来设置对应时钟的时间。tp参数所指定时间值如果不是时间精度的整数倍的话会被截短成整数倍,有些时钟是不能被设置的。
参数clock_id表示时钟类型,Linux提供下面这些时钟类型。
CLOCK_REALTIME
: 系统范围的实时时钟。系统范围是指系统的所有用户所有程序都使用。实时时钟是指系统现在的时间(就像Windows右下角的那个时间),这和gettimeofday
函数获取的系统时间是相同的。系统时间是可以修改的,所以可以使用clock_settime(CLOCK_REALTIME)
修改系统的时间,这个值是从Epoch开始的时间。CLOCK_REALTIME_COARSE
: 一个更快但时间粒度更大(即时间精确度没那么高)的CLOCK_REALTIME
。这个函数是Linux系统特有的,在2.6.32内核版本中首次出现CLOCK_MONOTONIC
:monotonic时间。monotonic是单调的意思,也就是说它只会单调递增,不能被人手动修改。POSIX只是说明它是一个单调时间,并没有指定从什么时候开始算起的单调时间。所以有些系统取了Epoch时间,而有些系统(比如Linux)就取boot启动的时间。但也并不影响它的本身意义CLOCK_MONOTONIC_COARSE
:一个更快但时间粒度更大(即时间精确度没那么高)的CLOCK_MONOTONIC
。这个函数是Linux系统特有的,在2.6.32内核版本中首次出现CLOCK_BOOTTIME
: 同CLOCK_MONOTONIC
一样,只是当系统被挂起时,一样会计时(CLOCK_MONOTONIC
不会)CLOCK_PROCESS_CPUTIME_ID
: 进程使用了多少CPU时间。该进程的所有线程所使用的CPU时间都会被统计进来CLOCK_THREAD_CPUTIME_ID
: 线程使用了多少CPU时间
更多信息请参考此处:>>>这里<<<。
历史上,在SystemV派生的系统实现中,调用stime(2)函数来设置系统时间,而在BSD派生的系统中调用settimeofday(2)设置系统时间。
stime
stime
头文件:<time.h>
定义:
int stime(time_t *t);
settimeofday
settimeofday
头文件:<sys/time.h> & <unistd.h>
定义:
int settimeofday(const struct timeval *tv, const struct timezone *tz);
注意,在Linux下,只有root 权限才能使用此函数修改时间。
返回值:成功则返回0,失败返回-1,错误代码存于errno。
错误代码:
EPERM
并非由root 权限调用settimeofday(),权限不够。
EINVAL
时区或某个数据是不正确的,无法正确设置时间。
gettimeofday
gettimeofday
头文件:<sys/time.h>
//Unix/Linux
定义:
int gettimeofday (struct timeval *restrict tp, void * restrict tzp);
tzp唯一的合法值为NULL,其他值将产生不确定的结果。某些平台支持tzp 说明时区,不过这完全依赖实现而定,SUS并没有对此定义。
SUSv4指定gettimeofday函数现在已弃用。
这里是一个支持时区版本的定义:
int gettimeofday (struct timeval * tv, struct timezone * tz);
timezone 结构定义为:
struct timezone
{
int tz_minuteswest; //和Greenwich 时间差了多少分钟
int tz_dsttime; //日光节约时间的状态
};
转换函数
gmtime,gmtime_s
gmtime,gmtime_s
头文件:<time.h> | <ctime>
定义:
struct tm *gmtime( const time_t *time );
struct tm *gmtime_s(const time_t *restrict time, struct tm *restrict result) //(since C11)
gmtime
将Epoch以来的时间(time参数指向的time_t值)转换为日历时间(此处日历时间,是现实意义的,而非专业术语),以 struct tm
格式表示的协调世界时 (utc)。结果存储在静态存储器中,并返回指向该静态存储器的指针。
gmtime_s
跟前者一样,只不过结果储存到用户提供的result参数所指向的地方。并且在运行时检测到以下错误,就调用当前设置的 constraint handler 函数:time参数或者result参数为空。
像所有的边界检查函数一样,只有当 __STDC_LIB_EXT1__
被实现所定义,并且用户在包含time.h之前,将__STDC_WANT_LIB_EXT1__
定义为整数常量1时 gmtime_s
才可以使用 。
gmtime
这个函数可能不是线程安全的。
POSIX 要求这个函数将errno设置为 EOVERFLOW
,如果因为参数太大而失败。
POSIX 定义了一个线程安全的替代gmtime_r
,类似于C11函数gmtime_s
,只是它不检查输入参数的有效性。
localtime,localtime_s
localtime,localtime_s
头文件:<time.h> | <ctime>
定义:
struct tm *localtime( const time_t *time );
struct tm *localtime_s(const time_t *restrict time, struct tm *restrict result);//(since C11)
localtime
将Epoch以来的时间(time参数指向的time_t值)转换为日历时间(此处日历时间,是现实意义的,而非专业术语),以 struct tm
格式表示的本地时间(考虑本地时区和夏令时)。结果存储在静态存储器中,并返回指向该静态存储器的指针。
localtime_s
跟前者一样,只不过结果储存到用户提供的result参数所指向的地方。并且在运行时检测到以下错误,就调用当前设置的 constraint handler 函数:time参数或者result参数为空。
像所有的边界检查函数一样,只有当 __STDC_LIB_EXT1__
被实现所定义,并且用户在包含time.h之前,将__STDC_WANT_LIB_EXT1__
定义为整数常量1时 localtime_s
才可以使用 。
localtime
这个函数可能不是线程安全的。
POSIX 要求这个函数将errno设置为 EOVERFLOW
,如果因为参数太大而失败。
POSIX 定义了一个线程安全的替代localtime_r
,类似于C11函数localtime_s
,只是它不检查输入参数的有效性。
posix 指定时区信息是由该函数决定的,就好像调用tzset读取环境变量TZ。
可以看到gmtime
和localtime
的差别在于一个转换为UTC时间,一个转换为本地时间(考虑本地时区和夏令时),当然如果你就在GreenWnich的话,那这两个时间是没有去别的。
mktime
mktime
头文件:<time.h> | <ctime>
定义:
time_t mktime( struct tm *time );
重新标准化由struct tm
对象表示的本地日历时间,并且转换为由time_t
表示的从Epoch开始的秒数。
time-> tm_wday
和time-> tm_yday
被忽略。不对时间值做超出范围检查。
time-> tm_isdst
的负值会导致mktime
尝试确定夏令时是否在指定时间内生效。
如果转换为time_t
成功,则会修改time参数所指的对象。时间的所有字段都会更新以适应其适当的范围。time-> tm_wday
和time-> tm_yday
是使用其他字段中可用的信息重新计算的。
所以即使我们不需要转换出的time_t
值,我们也可以使用它来计算time-> tm_wday
和time-> tm_yday
。
函数返回time_t
对象表示的从Epoch开始的秒数,如果时间不能表示为time_t
对象,则返回-1(在这种情况下,POSIX也需要将EOVERFLOW
存储在errno
中)。
注意:如果struct tm
对象是从std::get_time
或者POSIX strptime
或等价的函数中获得的,则tm_isdst
的值是不确定的,需要在调用mktime
之前明确地设置。
asctime, asctime_s
asctime, asctime_s
头文件:<time.h> | <ctime>
定义:
char* asctime( const struct tm* time_ptr );
errno_t asctime_s(char *buf, rsize_t bufsz, const struct tm *time_ptr);//(since C11)
asctime
函数将给定的日历时间tm转换为以下固定的25个字符格式的文本表示形式:Www Mmm dd hh:mm:ss yyyy\n
WWW : 一周中的第几天,也就是星期几的缩写,取自time_ptr->tm_wday
,是以下结果的其中之一Mon, Tue, Wed, Thu, Fri, Sat, Sun
Mmm : 月份名称的缩写,取自time_ptr->tm_mon
,是以下结果的其中之一Jan, Feb, Mar, Apr, May, Jun, Jul, Aug, Sep, Oct, Nov, Dec
dd : 2位数表示的号数,取自time_ptr->tm_mday
,格式相当于 sprintf
使用%2d
格式。
hh : 2位数表示的小时,取自time_ptr->tm_hour
,格式相当于 sprintf
使用%2d
格式。
mm : 2位数表示的分钟,取自time_ptr->tm_min
,格式相当于 sprintf
使用%2d
格式。
ss : 2位数表示的秒数,取自time_ptr->tm_sec
,格式相当于 sprintf
使用%2d
格式。
yyyy : 4位数表示的年份,取自time_ptr->tm_year + 1900
,格式相当于 sprintf
使用%4d
格式。
如果* time_ptr
的任何成员超出其正常范围,则行为是不确定的。
如果由time_ptr-> tm_year
指示的日历年有4位以上或小于1000年,则行为是不确定的。
该函数不支持本地化,并且换行符不能被删除。
该函数修改静态存储并且不是线程安全的。
asctime_s
跟前者一样,只不过结果储存到用户提供的buf参数所指向的缓冲区。并且在运行时检测到以下错误,就调用当前设置的 constraint handler 函数:
buf
或者time_ptr
为空指针bufsz
小于26或大于RSIZE_MAX
- 并非
* time_ptr
的所有成员都在正常范围内 time_ptr-> tm_year
表示的年份小于0或大于9999
像所有的边界检查函数一样,只有当 __STDC_LIB_EXT1__
被实现所定义,并且用户在包含time.h之前,将__STDC_WANT_LIB_EXT1__
定义为整数常量1时 localtime_s
才可以使用 。
对于asctime
,指向以上所述的保存日期和时间文本表示的以null作为结尾的静态字符串的指针。该字符串可以在asctime
和ctime
之间共享,并且可以在每次调用任何这些函数时被覆盖。
这个函数返回一个指向静态数据的指针,而且不是线程安全的。POSIX标记这个功能已经过时,建议使用strftime
。C标准也建议使用strftime
而不是asctime
和asctime_s
,因为strftime
更灵活,而且区域设置非常敏感。
POSIX限制未定义的行为仅限于当输出字符串长于25个字符,当timeptr-> tm_wday
或timeptr-> tm_mon
不在预期的范围内,或者当timeptr-> tm_year
超过INT_MAX-1990
时。
一些实现处理timeptr-> tm_mday == 0
,作为前一个月的最后一天。
对于asctime_s
,在成功时为零,在失败时为非零,在这种情况下,buf[0]
被设置为零(除非buf
是空指针或者bufsz
是零或大于RSIZE_MAX
)。
ctime, ctime_s
ctime, ctime_s
头文件:<time.h> | <ctime>
定义:
char* ctime( const time_t* time );
errno_t ctime_s(char *buffer, rsize_t bufsz, const time_t *time);//(since C11)
ctime
将自Epoch以来的给定time转换为当地日历,然后转换为文本表示,就像调用asctime(localtime(time))
一样。
结果为固定的25个字符格式的文本表示形式:Www Mmm dd hh:mm:ss yyyy\n
WWW : 一周中的第几天,也就是星期几的缩写,取自time_ptr->tm_wday
,是以下结果的其中之一Mon, Tue, Wed, Thu, Fri, Sat, Sun
Mmm : 月份名称的缩写,取自time_ptr->tm_mon
,是以下结果的其中之一Jan, Feb, Mar, Apr, May, Jun, Jul, Aug, Sep, Oct, Nov, Dec
dd : 2位数表示的号数,取自time_ptr->tm_mday
,格式相当于 sprintf
使用%2d
格式。
hh : 2位数表示的小时,取自time_ptr->tm_hour
,格式相当于 sprintf
使用%2d
格式。
mm : 2位数表示的分钟,取自time_ptr->tm_min
,格式相当于 sprintf
使用%2d
格式。
ss : 2位数表示的秒数,取自time_ptr->tm_sec
,格式相当于 sprintf
使用%2d
格式。
yyyy : 4位数表示的年份,取自time_ptr->tm_year + 1900
,格式相当于 sprintf
使用%4d
格式。
ctime_s
跟前者一样,只不过结果储存到用户提供的buf参数所指向的缓冲区。等价于asctime_s(buffer, bufsz, localtime_s(time, &(struct tm){0}))
。在运行时检测到以下错误,就调用当前设置的 constraint handler 函数:
buffer
或者time
为空指针bufsz
小于26或大于RSIZE_MAX
该功能不支持本地化。
对于ctime
,指向以上所述的保存日期和时间文本表示的以null作为结尾的静态字符串的指针。该字符串可以在asctime
和ctime
之间共享,并且可以在每次调用任何这些函数时被覆盖。
ctime_s
成功的话返回0,(在这种情况下,表示时间的字符串已经被写入缓冲区所指向的数组),或者在失败时返回非零(在这种情况下,终止空字符总是写入buffer[0]
,除非缓冲区是空指针或bufsz
是零或大于RSIZE_MAX
。
ctime
返回一个指向静态数据的指针,而且不是线程安全的。另外还修改了可以与gmtime
和localtime
共享的静态tm对象。POSIX标记这个功能已经过时,建议使用strftime
。C标准也建议使用strftime
而不是ctime
和ctime_s
,因为strftime
更灵活,而且区域设置非常敏感。
对于time_t
的值,ctime
的行为可能未定义,导致字符串长于25个字符(例如,年份10000)。
strftime
strftime
头文件:<time.h> | <ctime>
定义:
size_t strftime( char * str, size_t count,const char * format, const struct tm * time );//(until C99)
size_t strftime( char *restrict str, size_t count, const char *restrict format, const struct tm *restrict time );//(since C99)
std::size_t strftime( char* str, std::size_t count, const char* format, const std::tm* time );
该函数根据格式字符串将日期和时间信息从给定的日历时间转换为以null结尾的多字节字符串str。最多可以写入count字节的数据。
格式字符串包含零个或多个转换说明符和普通字符(%除外)。所有普通字符,包括终止空字符,都会被复制到输出字符串而不做任何修改。每个转换规范都以%字符开始,可以选择后跟E 或 O修饰符(如果语言环境不支持,则忽略),然后是决定说明符行为的字符。可用的格式说明符请参考此处:
http://en.cppreference.com/w/c/chrono/strftime
http://en.cppreference.com/w/cpp/chrono/c/strftime
函数返回写入str所指向的字符数组中的字节数,不包括'\0'
。如果在整个字符串可以被存储之前达到限制数量,则返回0,并且内容是未定义的。
wcsftime
wcsftime
头文件:<wchar.h>|<cwchar>
定义:
size_t wcsftime( wchar_t* str, size_t count, const wchar_t* format, tm* time );//(since C95)
std::size_t wcsftime( wchar_t* str, std::size_t count, const wchar_t* format, const std::tm* time );
该函数根据格式字符串将日期和时间信息从给定的日历时间转换为以null结尾的宽字节字符串str。最多可以写入count字节的数据。
格式字符串包含零个或多个转换说明符和普通字符(%除外)。所有普通字符,包括终止空字符,都会被复制到输出字符串而不做任何修改。每个转换规范都以%字符开始,可以选择后跟E 或 O修饰符(如果语言环境不支持,则忽略),然后是决定说明符行为的字符。可用的格式说明符请参考此处:
http://en.cppreference.com/w/c/chrono/wcsftime
http://en.cppreference.com/w/cpp/chrono/c/wcsftime
函数返回写入str所指向的字符数组中的字节数,不包括L'\0'
。如果在整个字符串可以被存储之前达到限制数量,则返回0,并且内容是未定义的。
可以看到wcsftime
与strftime
的区别就是,一个是宽字节版本的,一个不是。
strptime
strptime
头文件:<time.h>``//Unix/Linux
定义:
char *strptime(const char *restrict buf, const char *restrict format,
struct tm *restrict tm);
strptime()
函数应该使用格式指定的格式将buf
指向的字符串转换为存储在tm
指向的tm
结构中的值。
该格式由零个或多个指令组成。每个指令由以下之一组成:一个或多个空格字符(由isspace()指定);一个普通的字符(既不是’%’也不是一个空白字符);或转换规范。
每个转换规范由一个“%”字符组成,后面跟着一个转换字符,用于指定所需的替换。
应用程序应确保在任何两个转换规范之间有空格或其他非字母数字字符。转换规范请参考此处:
http://pubs.opengroup.org/onlinepubs/009695399/functions/strptime.html
difftime
difftime
头文件:<time.h> | <ctime>
定义:
double difftime( time_t time_end, time_t time_beg );
该函数就是计算的 time_end - time_beg
的差值,可是结果为什么是double呢?这是因为在POSIX系统中,time_t以秒为单位,difftime相当于算术相减,但C和C++允许time_t的小数单位。
还应该注意的是 如果time_end 小于了 time_beg 那么此函数的行为是未定义的。这一点应该是函数调用者应该保证的。
get_time
get_time
头文件:<iomanip>
定义:
template< class CharT >
/*unspecified*/ get_time( std::tm* tmb, const CharT* fmt );//(since C++11)
当在表达式 in >> get_time(tmb,fmt)
中使用时,根据当前嵌入在输入流中的语言环境的std :: time_get
,根据格式化字符串fmt
将字符输入解析为time/date值。 结果值存储在由tmb
指向的std :: tm
对象中。
格式字符串包含零个或多个转换说明符和普通字符(%除外)。所有普通字符,包括终止空字符,都会被复制到输出字符串而不做任何修改。每个转换规范都以%字符开始,可以选择后跟E 或 O修饰符(如果语言环境不支持,则忽略),然后是决定说明符行为的字符。可用的格式说明符请参考此处:
http://en.cppreference.com/w/cpp/io/manip/get_time
该函数返回未指定类型的对象,如果in
是类型为std::basic_istream<CharT, Traits>
的input stream的名称,则in >> get_time(tmb, fmt)
的行为如同下面代码:
typedef std::istreambuf_iterator<CharT, Traits> Iter;
typedef std::time_get<CharT, Iter> TimeGet;
std::ios_base::iostate err = std::ios_base::goodbit;
const TimeGet& tg = std::use_facet<TimeGet>(in.getloc());
tg.get(Iter(in.rdbuf()), Iter(), in, err, tmb, fmt, fmt + traits::length(fmt));
if (err != std::ios_base::goodbit)
in.setstate(err);
如函数调用的std :: time_get :: do_get
中指定的那样,如果这个函数将* tmb
中的字段置零,而这些字段不是由fmt中出现的转换说明符直接设置的,那么行为是未定义的,所以可移植的程序应在调用std :: get_time
之前,初始化*tmb
的每一个字段为0。
注意:选择clang来观察输出时你会发现。libstdc++没有正确实现%b说明符:bug 78714
put_time
put_time
头文件:<iomanip>
定义:
template< class CharT >
/*unspecified*/ put_time( const std::tm* tmb, const CharT* fmt );//(since C++11)
当在表达式out << put_time(tmb,fmt)
中使用时,根据格式字符串fmt将time/date信息从给定的日历时间tmb转换为字符串,就像通过调用std :: strftime
,std :: wcsftime
,或模拟(取决于CharT),根据当前在输出流中输出的语言环境的std :: time_put
。
格式字符串包含零个或多个转换说明符和普通字符(%除外)。所有普通字符,包括终止空字符,都会被复制到输出字符串而不做任何修改。每个转换规范都以%字符开始,可以选择后跟E 或 O修饰符(如果语言环境不支持,则忽略),然后是决定说明符行为的字符。可用的格式说明符请参考此处:
http://en.cppreference.com/w/cpp/io/manip/put_time
该函数返回未指定类型的对象,如果in
是类型为std::basic_istream<CharT, Traits>
的input stream的名称,则out << put_time(tmb, fmt)
的行为如同下面代码:
typedef std::ostreambuf_iterator<CharT, Traits> Iter;
typedef std::time_put<CharT, Iter> TimePut;
const TimePut& tp = std::use_facet<TimePut>(out.getloc());
const Iter end = tp.put(Iter(out.rdbuf()), out, out.fill(), tmb, fmt, fmt + Traits::length(fmt));
if (end.failed())
out.setstate(std::ios_base::badbit);
除了以上函数以外,Linux还定义了一系列用于struct timeval的操作函数。比如相加减,清空,比较。
#include <sys/time.h>
void timeradd(struct timeval *a, struct timeval *b,struct timeval *res);//res = a + b
void timersub(struct timeval *a, struct timeval *b,struct timeval *res);//res = a - b
void timerclear(struct timeval *tvp);
int timerisset(struct timeval *tvp);
int timercmp(struct timeval *a, struct timeval *b, CMP);
参数CMP就是我们平常用的比较符号<、>=、!=等等。
这些函数的详细介绍大家可以从 man 手册中查到,这里不详细介绍了。
总结
到这里,时间相关函数就讲完了,如有遗漏还忘各位看官轻喷并欢迎指正。最后我用一张图来概括一下以上知识: