C时间类型与操作
在计算机中,计算时间,C语言扮演了一次上帝的角色,它说,让时间从1970年1月1日零点零分算起吧,用一个数据,存储这一起始点到当前总秒数这个数据的类型,称为“time_t”.
【小提示】:不精确的计时
C语言提供的计时,并不精确,因为它不主动处理科学上的“闰秒”现象,比如2012年6月30日23点59分59秒之后,并不是7月1号的第1秒,而是2012年6月30日23点59分60秒,然而C语言不理解这一秒。
1. time_t:
“time_t”实际是一个类型别名(typedef),具体类型实现觉得,通常精度不小于整数类型。
当前使用32位GCC编译,time_t的size为4,换为64位GCC编译,则为8,这符合C/C++语言中“long”类型的长度规定;从C++11起,标准规定应为实数类型表达。后面为了同一,我们都使用32位版本。
2. time(time_t*)函数
当下距离C语言的时间原点已经过去了多少秒?C语言提供了“time()”函数返回这个数值
time_t time(time_t *time);
入参是time_t的指针,出参也是time_t,但其实入参也是用来返回结果的,所以可以这样得到秒数
time_t seconds = 0;
time(&seconds);
更直观和简便的做法,是传入空指针,通过返回值获得结果:
time_t seconds = time(nullptr);
例如:
cout << "距离1970年1月1日零点零分已经过去了:"
<< time(nullptr) << "秒。" << endl;
3. tm结构和localtime函数
localtime函数,可以将前面提到的“秒数”,转换为具体的日历时间。(并且它会根据操作系统提供的时区进行调整,比如北京是东八区),日历时间使用名为“tm”的结构存储,它有如下成员数据:
struct tm
{
int tm_sec; //秒[0,60],多数的1秒范围,可用于手工处理闰秒
int tm_min; //分[0,59]
int tm_hour;//小时[0,23]
int tm_mday;//本月第几天[1,31],从1起,和生活中的日历一致
int tm_mon; //月[0,11],对不起,它从0开始,和生活经验不一致
int tm_year;//年,从1900年开始,而不是从1970年开始
int tm_wday;//星期几,[0,6]从0开始,并且0代表星期天
int tm_yday;//一年中第几天[0,365],同样从0开始。
int tm_isdst;//1、0,表示是否夏时历,或为-1表示未知,中国不采用
};
对于一个东方人来讲,这个日期结构充满陷阱,所以一定要认真看各个成员的注释。
localtime函数声明如下:
tm* localtime(const time_t * time);
要知道现在是什么时候,可以先得到当前时间time_t,在将它转换为tm结构:
【课堂作业】:公历转农历
上网百度“C语言 公历 农历”,应该能搜索出不少使用C语言将公历日期转换为农历的代码。复制一份,阅读,理解,编译,并检查正确性。
4. clock_t
clock()函数,返回当前程序启动后的计时。因为关注的“历史”变短,所以关注的精度得以提高。
它返回的整数,代表当前程序本次启动之后的“CPU时间”。不过,根据平台不同,这里的“CPU时间”所指的真实含义,大有不同,这一点必须注意,特别是编写跨平台程序时。
“clock”的字面意思(你可以把这个词当成“象声词”理解),是时钟的又一次“滴答”,有些人认为时钟一次滴答不就是一秒吗?这个可不一定,石英钟或许如此,老式座钟滴答几次才一秒,是需要物理学计算的。
到了操作系统,Windows环境(包括mingw环境)下滴答1000次才一秒,而在Linux环境下滴答
1 000 000 次才是1秒,显然Linux追求更高的精度。
【危险】:Windows和Linux下,clock()的关键差异
有关不同操作系统,规定一秒钟到底包含多少次“clock”的差异,只许通过一个宏“CLOCKS_PER_SEC”即可解决,真正让人“摔跟斗”的差异是同样调用clock(),Windows返回的是程序启动到现在的时间,和CPU其实无关,而Linux返回的是程序启动到现在它所占用的CPU计算时间。
想想同一时刻里,你的电脑前台或后台中在运行的程序,可能上百个,而你的CPU可能就四核或八核,所以Linux下clock()返回的数值,将远远小于Windows下的值。
clock()最常用来计时某段代码的运行时长,我们曾经拿它对比过vector和list的插入性能。今天又知道它在不同操作系统下的关键差异。
不过有关clock()函数还有个危险点,那就是虽然它通常和time_t是同一类型的别名(比如long),但由于它对精度要求高,所以容易溢出。
以32位Windows为例,clock_t使用long(有符号)表达,因此最大值是(2的31次方 - 1),因此程序连续运行24.855 134 803天之后clock()返回值即溢出。
【课堂作业】:亲眼目睹clock_t的溢出
写一段出程序,前后两次获取clock()返回值,并想办法让第二次所得到的值溢出(打印到屏幕是负数)——办法之一是开个程序不退出,再加不关机等它25天。
长期以来,C++程序员都直接使用C标准函数处理日期时间,它们的接口简单直接,但也有着这样或那样的陷阱,为此,下面介绍C++新标准库以及boost提供的多项时间操作。