c与c++中的time相关函数
副标题#e#
本文从先容基本观念入手,探讨了在C/C++中对日期和时间操纵所用到的数据布局和函数,并对计时、时间的获取、时间的计较和显示名目等方面举办了叙述。本文还通过大量的实例向你展示了time.h头文件中声明的各类函数和数据布局的具体利用要领。
要害字:UTC(世界尺度时间),Calendar Time(日历时间),epoch(时间点),clock tick(时钟计时单位)
1.观念
在C/C++中,对字符串的操纵有许多值得留意的问题,同样,C/C++对时间的操纵也有很多值得各人留意的处所。最近,在技能群中有许多网友也多次问到 过C++语言中对时间的操纵、获取和显示等等的问题。下面,在这篇文章中,笔者将主要先容在C/C++中时间和日期的利用要领。
通过进修很多C/C++库,你可以有许多操纵、利用时间的要领。但在这之前你需要相识一些“时间”和“日期”的观念,主要有以下几个:
Coordinated Universal Time(UTC):协调世界时,又称为世界尺度时间,也就是各人所熟知的格林威治尺度时间(Greenwich Mean Time,GMT)。好比,中海内陆的时间与UTC的时差为+8,也就是UTC+8.美国事UTC-5.
Calendar Time:日历时间,是用“从一个尺度时间点到此时的时间颠末的秒数”来暗示的时间。这个尺度时间点对差异的编译器来说会有所差异,但对一个编译系统来 说,这个尺度时间点是稳定的,该编译系统中的时间对应的日历时间都通过该尺度时间点来权衡,所以可以说日历时间是“相对时间”,可是无论你在哪一个时区,在同一时刻对同一个尺度时间点来说,日历时间都是一样的。
epoch:时间点。时间点在尺度C/C++中是一个整数,它用此时的时间和尺度时间点相差的秒数(近日历时间)来暗示。
clock tick:时钟计时单位(而不把它叫做时钟滴答次数),一个时钟计时单位的时间是非是由CPU节制的。一个clock tick不是CPU的一个时钟周期,而是C/C++的一个根基计时单元。
我们可以利用ANSI尺度库中的time.h头文件。这个头文件中界说的时间和日期所利用的要领,无论是在布局界说,照旧定名,都具有明明的C语言气势气魄。下面,我将说明在C/C++中奈何利用日期的时间成果。
2. 计时
C/C++中的计时函数是clock(),而与其相关的数据范例是clock_t.在MSDN中,查得对clock函数界说如下:
clock_t clock( void );
这个函数返回从“开启这个措施历程”到“措施中挪用clock()函数”时之间的CPU时钟计时单位(clock tick)数,在MSDN中称之为挂钟时间(wal-clock)。个中clock_t是用来生存时间的数据范例,在time.h文件中,我们可以找到对 它的界说:
#ifndef _CLOCK_T_DEFINED
typedef long clock_t;
#define _CLOCK_T_DEFINED
#endif
很明明,clock_t是一个长整形数。在time.h文件中,还界说了一个常量CLOCKS_PER_SEC,它用来暗示一秒钟会有几多个时钟计时单位,其界说如下:
#define CLOCKS_PER_SEC ((clock_t)1000)
可以看到每过千分之一秒(1毫秒),挪用clock()函数返回的值就加1.下面举个例子,你可以利用公式clock()/CLOCKS_PER_SEC来计较一个历程自身的运行时间:
void elapsed_time()
{
printf("Elapsed time:%u secs.\n",clock()/CLOCKS_PER_SEC);
}
虽然,你也可以用clock函数来计较你的呆板运行一个轮回可能处理惩罚其它事件到底花了几多时间:
#include “stdio.h”
#include “stdlib.h”
#include “time.h”
int main( void )
{
long i = 10000000L;
clock_t start, finish;
double duration;
/* 丈量一个事件一连的时间*/
printf( "Time to do %ld empty loops is ", i );
start = clock();
while( i-- ) ;
finish = clock();
duration = (double)(finish - start) / CLOCKS_PER_SEC;
printf( "%f seconds\n", duration );
system("pause");
}
在笔者的呆板上,运行功效如下:
Time to do 10000000 empty loops is 0.03000 seconds
上面我们看到时钟计时单位的长度为1毫秒,那么计时的精度也为1毫秒,那么我们可不行以通过改变CLOCKS_PER_SEC的界说,通过把它界说的大一些,从而使计时精度更高呢?通过实验,你会发明这样是不可的。在尺度C/C++中,最小的计时单元是一毫秒。
#p#副标题#e#
3.与日期和时间相关的数据布局
在尺度C/C++中,我们可通过tm布局来得到日期和时间,tm布局在time.h中的界说如下:
#ifndef _TM_DEFINED
struct tm {
int tm_sec; /* 秒 – 取值区间为[0,59] */
int tm_min; /* 分 - 取值区间为[0,59] */
int tm_hour; /* 时 - 取值区间为[0,23] */
int tm_mday; /* 一个月中的日期 - 取值区间为[1,31] */
int tm_mon; /* 月份(从一月开始,0代表一月) - 取值区间为[0,11] */
int tm_year; /* 年份,其值便是实际年份减去1900 */
int tm_wday; /* 礼拜 – 取值区间为[0,6],个中0代表礼拜天,1代表礼拜一,以此类推 */
int tm_yday; /* 从每年的1月1日开始的天数 – 取值区间为[0,365],个中0代表1月1日,1代表1月2日,以此类推 */
int tm_isdst; /* 夏令时标识符,实行夏令时的时候,tm_isdst为正。不实行夏令时的进候,tm_isdst为0;不相识环境时,tm_isdst()为负。*/
};
#define _TM_DEFINED
#endif
ANSI C尺度称利用tm布局的这种时间暗示为解析时间(broken-down time)。
#p#分页标题#e#
而日历时间(Calendar Time)是通过time_t数据范例来暗示的,用time_t暗示的时间(日历时间)是从一个时间点(譬喻:1970年1月1日0时0分0秒)到此时的秒数。在time.h中,我们也可以看到time_t是一个长整型数:
#ifndef _TIME_T_DEFINED
typedef long time_t; /* 时间值 */
#define _TIME_T_DEFINED /* 制止反复界说 time_t */
#endif
各人大概会发生疑问:既然time_t实际上是长整型,到将来的某一天,从一个时间点(一般是1970年1月1日0时0分0秒)到当时的秒数(近日 历时间)超出了长整形所能暗示的数的范畴怎么办?对time_t数据范例的值来说,它所暗示的时间不能晚于2038年1月18日19时14分07秒。为了 可以或许暗示更长远的时间,一些编译器厂商引入了64位甚至更长的整形数来生存日历时间。好比微软在Visual C++中回收了__time64_t数据范例来生存日历时间,并通过_time64()函数来得到日历时间(而不是通过利用32位字的time()函 数),这样就可以通过该数据范例生存3001年1月1日0时0分0秒(不包罗该时间点)之前的时间。
在time.h头文件中,我们还可以看到一些函数,它们都是以time_t为参数范例或返回值范例的函数:
double difftime(time_t time1, time_t time0);
time_t mktime(struct tm * timeptr);
time_t time(time_t * timer);
char * asctime(const struct tm * timeptr);
char * ctime(const time_t *timer);
另外,time.h还提供了两种差异的函数将日历时间(一个用time_t暗示的整数)转换为我们平时看到的把年代日时分秒分隔显示的时间名目tm:
struct tm * gmtime(const time_t *timer);
struct tm * localtime(const time_t * timer);
通过查阅MSDN,我们可以知道Microsoft C/C++ 7.0中时间点的值(time_t工具的值)是从1899年12月31日0时0分0秒到该时间点所颠末的秒数,而其它各类版本的Microsoft C/C++和所有差异版本的Visual C++都是计较的从1970年1月1日0时0分0秒到该时间点所颠末的秒数。
4.与日期和时间相关的函数及应用
在本节,我将向各人展示奈何操作time.h中声明的函数对时间举办操纵。这些操纵包罗取当前时间、计较时距离断、以差异的形式显示时间等内容。
4.1 得到日历时间
我们可以通过time()函数来得到日历时间(Calendar Time),其原型为:
time_t time(time_t * timer);
假如你已经声明白参数timer,你可以从参数timer返回此刻的日历时间,同时也可以通过返回值返回此刻的日历时间,即从一个时间点(譬喻: 1970年1月1日0时0分0秒)到此刻此时的秒数。假如参数为空(NUL),函数将只通过返回值返回此刻的日历时间,好比下面这个例子用来显示当前的日 历时间:
#include "time.h"
#include "stdio.h"
int main(void)
{
struct tm *ptr;
time_t lt;
lt =time(NUL);
printf("The Calendar Time now is %d\n",lt);
return 0;
}
运行的功效与其时的时间有关,我其时运行的功效是:
The Calendar Time now is 1122707619
个中1122707619就是我运行措施时的日历时间。即从1970年1月1日0时0分0秒到此时的秒数。
4.2 得到日期和时间
这里说的日期和时间就是我们平时所说的年、月、日、时、分、秒等信息。从第2节我们已经知道这些信息都生存在一个名为tm的布局体中,那么如何将一个日历时间生存为一个tm布局的工具呢?
个中可以利用的函数是gmtime()和localtime(),这两个函数的原型为:
struct tm * gmtime(const time_t *timer);
struct tm * localtime(const time_t * timer);
个中gmtime()函数是将日历时间转化为世界尺度时间(即格林尼治时间),并返回一个tm布局体来生存这个时间,而localtime()函数 是将日历时间转化为当地时间。好比此刻用gmtime()函数得到的世界尺度时间是2005年7月30日7点18分20秒,那么我用localtime ()函数在中国地域得到的当地时间会比世界尺度时间晚8个小时,即2005年7月30日15点18分20秒。下面是个例子:
#p#分页标题#e#
#include "time.h"
#include "stdio.h"
int main(void)
{
struct tm *local;
time_t t;
t=time(NUL);
local=localtime(&t);
printf("Local hour is: %d\n",local->tm_hour);
local=gmtime(&t);
printf("UTC hour is: %d\n",local->tm_hour);
return 0;
}
运行功效是:
Local hour is: 15 UTC hour is: 7
4.3 牢靠的时间名目
我们可以通过asctime()函数和ctime()函数将时间以牢靠的名目显示出来,两者的返回值都是char*型的字符串。返回的时间名目为:
礼拜几 月份 日期 时:分:秒 年\n\0
譬喻:Wed Jan 02 02:03:55 1980\n\0
个中\n是一个换行符,\0是一个空字符,暗示字符串竣事。下面是两个函数的原型:
char * asctime(const struct tm * timeptr);
char * ctime(const time_t *timer);
个中asctime()函数是通过tm布局来生成具有牢靠名目标生存时间信息的字符串,而ctime()是通过日历时间来生成时间字符串。这样的 话,asctime()函数只是把tm布局工具中的各个域填到时间字符串的相应位置就行了,而ctime()函数需要先参照当地的时间配置,把日历时间转 化为当地时间,然后再生成名目化后的字符串。在下面,假如t是一个非空的time_t变量的话,那么:
printf(ctime(&t));
等价于:
struct tm *ptr;
ptr=localtime(&t);
printf(asctime(ptr));
那么,下面这个措施的两条printf语句输出的功效就是差异的了(除非你将当地时区设为世界尺度时间地址的时区):
#include "time.h"
#include "stdio.h"
int main(void)
{
struct tm *ptr;
time_t lt;
lt =time(NUL);
ptr=gmtime(<);
printf(asctime(ptr));
printf(ctime(<));
return 0;
}
运行功效:
Sat Jul 30 08:43:03 2005
Sat Jul 30 16:43:03 2005
4.4 自界说时间名目
我们可以利用strftime()函数将时间名目化为我们想要的名目。它的原型如下:
size_t strftime(
char *strDest,
size_t maxsize,
const char *format,
const struct tm *timeptr
);
我们可以按照format指向字符串中名目呼吁把timeptr中生存的时间信息放在strDest指向的字符串中,最多向strDest中存放maxsize个字符。该函数返回向strDest指向的字符串中安排的字符数。
函数strftime()的操纵有些雷同于sprintf():识别以百分号(%)开始的名目呼吁荟萃,名目化输出功效放在一个字符串中。名目化命 令说明串strDest中各类日期和时间信息简直切暗示要领。名目串中的其他字符原样放进串中。名目呼吁列在下面,它们是区分巨细写的。
%a 礼拜几的简写
%A 礼拜几的全称
%b 月分的简写
%B 月份的全称
%c 尺度的日期的时间串
%C 年份的后两位数字
%d 十进制暗示的每月的第几天
%D 月/天/年
%e 在两字符域中,十进制暗示的每月的第几天
%F 年-月-日
%g 年份的后两位数字,利用基于周的年
%G 年分,利用基于周的年
%h 简写的月份名
%H 24小时制的小时
%I 12小时制的小时
%j 十进制暗示的每年的第几天
%m 十进制暗示的月份
%M 十时制暗示的分钟数
%n 新行符
%p 当地的AM或PM的等价显示
%r 12小时的时间
%R 显示小时和分钟:hh:mm
%S 十进制的秒数
%t 程度制表符
%T 显示时分秒:hh:mm:ss
%u 每周的第几天,礼拜一为第一天 (值从0到6,礼拜一为0)
%U 第年的第几周,把礼拜日做为第一天(值从0到53)
%V 每年的第几周,利用基于周的年
%w 十进制暗示的礼拜几(值从0到6,礼拜天为0)
%W 每年的第几周,把礼拜一做为第一天(值从0到53)
%x 尺度的日期串
%X 尺度的时间串
%y 不带世纪的十进制年份(值从0到99)
%Y 带世纪部门的十进制年份
%z,%Z 时区名称,假如不能获得时区名称则返回空字符。
%% 百分号
假如想显示此刻是几点了,并以12小时制显示,就象下面这段措施:
#p#分页标题#e#
#include “time.h”
#include “stdio.h”
int main(void)
{
struct tm *ptr;
time_t lt;
char str[80];
lt=time(NUL);
ptr=localtime(<);
strftime(str,100,"It is now %I %p",ptr);
printf(str);
return 0;
}
其运行功效为:
It is now 4PM
而下面的措施则显示当前的完整日期:
#include <stdio.h>
#include <time.h>
void main( void )
{
struct tm *newtime;
char tmpbuf[128];
time_t lt1;
time( <1 );
newtime=localtime(<1);
strftime( tmpbuf, 128, "Today is %A, day %d of %B in the year %Y.\n", newtime);
printf(tmpbuf);
}
运行功效:
Today is Saturday,day 30 of July in the year 2005.
4.5 计较一连时间的长度
有时候在实际应用中要计较一个事件一连的时间长度,好比计较打字速度。在第1节计时部门中,我已经用clock函数举了一个例子。Clock()函数可以准确到毫秒级。同时,我们也可以利用difftime()函数,但它只能准确到秒。该函数的界说如下:
double difftime(time_t time1,time_t time0);
固然该函数返回的以秒计较的时距离断是double范例的,但这并不说明该时间具有同double一样的准确度,这是由它的参数以为的(time_t是以秒为单元计较的)。好比下面一段措施:
#include "time.h"
#include "stdio.h"
#include "stdlib.h"
int main(void)
{
time_t start,end;
start = time(NUL);
system("pause");
end = time(NUL);
printf("The pause used %f seconds.\n",difftime(end,start));//<-
system("pause");
return 0;
}
运行功效为:
请按任意键继承。 . .
The pause used 2.000000 seconds.
请按任意键继承。 . .
可以想像,暂停的时间并不那么巧是整整2秒钟。其实,你将上面措施的带有“//<-”注释的一行用下面的一行代码替换:
printf("The pause used %f seconds.\n",end-start);
其运行功效是一样的。
4.6 解析时间转化为日历时间
这里说的解析时间就是以年、月、日、时、分、秒平分量生存的时间布局,在C/C++中是tm布局。我们可以利用mktime()函数将用tm布局暗示的时间转化为日历时间。其函数原型如下:
time_t mktime(struct tm * timeptr);
其返回值就是转化后的日历时间。这样我们就可以先拟定一个解析时间,然后对这个时间举办操纵了,下面的例子可以计较出1997年7月1日是礼拜几:
#include "time.h"
#include "stdio.h"
#include "stdlib.h"
int main(void)
{
struct tm t;
time_t t_of_day;
t.tm_year=1997-1900;
t.tm_mon=6;
t.tm_mday=1;
t.tm_hour=0;
t.tm_min=0;
t.tm_sec=1;
t.tm_isdst=0;
t_of_day=mktime(&t);
printf(ctime(&t_of_day));
return 0;
}
运行功效:
Tue Jul 01 00:00:01 1997
此刻留意了,有了mktime()函数,是不是我们可以操纵此刻之前的任何时间呢?你可以通过这种步伐算出1945年8月15号是礼拜几吗?谜底是否认的。因为这个时间在1970年1月1日之前,所以在大大都编译器中,这样的措施固然可以编译通过,但运行时会异常终止。