linux下存储时间常见的有两种存储方式,一个是从1970年到现在经过了多少秒,一个是用一个结构来分别存储年月日时分秒的。
time_t 这种类型就是用来存储从1970年到现在经过了多少秒,要想更精确一点,可以用结构struct timeval,它精确到微妙。
struct timeval
{
long tv_sec; /*秒*/
long tv_usec; /*微秒*/
};
直接存储年月日的是一个结构:
struct tm
{
int tm_sec; /*秒,正常范围0-59, 但允许至61*/
int tm_min; /*分钟,0-59*/
int tm_hour; /*小时, 0-23*/
int tm_mday; /*日,即一个月中的第几天,1-31*/
int tm_mon; /*月, 从一月算起,0-11*/
int tm_year; /*年, 从1900至今已经多少年*/
int tm_wday; /*星期,一周中的第几天, 从星期日算起,0-6*/
int tm_yday; /*从今年1月1日到目前的天数,范围0-365*/
int tm_isdst; /*日光节约时间的旗标*/
};
需要特别注意的是,年份是从1900年起至今多少年,而不是直接存储如2008年,月份从0开始的,0表示一月,星期也是从0开始的, 0表示星期日,1表示星期一。
常用的时间函数:
#include <time.h>
char *asctime(const struct tm* timeptr);
将结构中的信息转换为真实世界的时间,以字符串的形式显示
char *ctime(const time_t *timep);
将timep转换为真实世界的时间,以字符串显示,它和asctime不同就在于传入的参数形式不一样
double difftime(time_t time1, time_t time2);
返回两个时间相差的秒数
int gettimeofday(struct timeval *tv, struct timezone *tz);
返回当前距离1970年的秒数和微妙数,后面的tz是时区,一般不用
函数说明
gettimeofday()会把目前的时间有tv所指的结构返回,当地时区的信息则放到tz所指的结构中。
timezone 结构定义为:
struct timezone{
int tz_minuteswest; /*和Greenwich时间差了多少分钟*/
int tz_dsttime; /*日光节约时间的状态*/
};
上述两个结构都定义在/usr/include/sys/time.h。tz_dsttime所代表的状态如下
DST_NONE /*不使用*/
DST_USA /*美国*/
DST_AUST /*澳洲*/
DST_WET /*西欧*/
DST_MET /*中欧*/
DST_EET /*东欧*/
DST_CAN /*加拿大*/
DST_GB /*大不列颠*/
DST_RUM /*罗马尼亚*/
DST_TUR /*土耳其*/
DST_AUSTALT /*澳洲(1986年以后)*/
返回值
成功则返回0,失败返回-1,错误代码存于errno。附加说明EFAULT指针tv和tz所指的内存空间超出存取权限。
范例
可以看出,使用这种方式计时,精度可达微秒,也就是10-6秒。进行计时的时候,我们需要前后调用两次gettimeofday,然后计算中间的差值:
gettimeofday( &start, NULL );
foo();
gettimeofday( &end, NULL );
timeuse = 1000000 * ( end.tv_sec -start.tv_sec ) + end.tv_usec - start.tv_usec;
timeuse /= 1000000;
struct tm* gmtime(const time_t *timep);
将time_t表示的时间转换为没有经过时区转换的UTC时间,是一个struct tm结构指针
stuct tm* localtime(const time_t *timep);
和gmtime类似,但是它是经过时区转换的时间。
time_t mktime(struct tm* timeptr);
将struct tm 结构的时间转换为从1970年至今的秒数
time_t time(time_t *t);
取得从1970年1月1日至今的秒数。
头文件 #include <time.h>
定义函数 struct tm *localtime(const time_t * timep);
函数说明 localtime()将参数timep 所指的time_t 结构中的信息转换成真实世界所使用的时间日期表示方法,
返回值返回结构tm 代表目前的当地时间.
范例
#include<time.h>
main()
{
char *wday[] ={"Sun", "Mon", "Tue", "Wed","Thu", "Fri", "Sat"};
time_t timep;
struct tm *p;
time(&timep);
p =localtime(&timep); //取得当地时间
printf ("%d%d%d", (1900+p->tm_year), (l+p->tm_mon), p->tm_mday);
printf("%s%d:%d:%d\n",wday[p->tm_wday], p->tm_hour, p->tm_min, p->tm_sec);
}
我们可以使用time调用获取当前的时间,注意,这是以UTC表示的机器时间——自1970年1月1日0点以来的秒数,接着我们用localtime调用可以将time获取的时间转换为本地时间,从UTC 转换到本地时间会依靠时区信息进行调整。对于一个daemon进程而言,如果每隔一段时间用time和localtime调用就可以定期获取当前时间的数值,但是如果在这个期间发生了时区设置转换会怎样呢? 或许你会觉得,那一定会出大问题——时区变了,localtime也会出现很大的调整。如果时区的设置变化了,localtime转换的时间依然与之前没有什么不同,除非程序再次启动。
Linux的时区设置通过几个不同的途径完成。一方面可以通过设置环境变量TZ来指定时区,例如TZ=Asia/Shanghai,可以将时区指定为上海所在的时区,时区的配置出现在/usr/share/zoneinfo/Asia/Shanghai文件当中(Redhat环境);如果没有指定TZ环境变量,那么缺省的时区配置文件可以用/etc/localtime来获得,这个文件可能是一个符号链接指向真实的文件,也有可能就是将/usr/share/zoneinfo下的文件复制过来达到所要的结果。由于环境变量由各个进程组单独继承,那么在设置时区之后很难改变其他进程组的环境变量,所以一般的系统很少直接设置TZ环境变量,而是由/etc/localtime文件来指示时区位置。
既然如此,为什么设置了时区以后,已经运行的daemon程序在使用localtime函数调用时没有使用新时区呢?这个可以通过glibc的源码来回答。
localtime等涉及到本地所在时区的函数在调用的时候会先调用tzset这个函数,这一点可以通过tzset函数的manpage看出来。tzset完成的工作是把当前时区信息(通过TZ环境变量或者/etc/localtime)读入并缓冲。事实上tzset在实现的时候是通过内部的 tzset_internal函数来完成的,显式的调用tzset会以显式的方式告知tzset_internal,而单独调用localtime的时候是以隐式的方式告知tzset_internal,前者将强制tzset不管何种情况一律重新加载TZ信息或者/etc/localtime,而后者则是只有在TZ发生变化,或者加载文件名发生变化的时候才会再次加载时区信息。因此,如果只是/etc/localtime的内容发生了变化,而文件名"/etc/localtime"没有变化,则不会再次加载时区信息,导致localtime函数调用仍然以老时区转换UTC时间到本地时间。
结论:对localtime的反复调用,如果要考虑时区变化的因素,最好显式的调用一次tzset。或许今后的glibc库会修正这个bug?至少我在glibc-2.3.5和2.4的版本上还看到这个问题。
函数名称: tzset
函数原型: void tzset(void)
函数功能: UNIX兼容函数,用于得到时区,在DOS环境下无用途
所属文件: <time.h>
#include <time.h>
#include <stdlib.h>
#include <stdio.h>
int main()
{
time_t td;
putenv("TZ=PST8PDT");
tzset();
time(&td);
printf("Currenttime=%s",asctime(localtime(&td)));
return 0;
}
clock_gettime( ) 提供了纳秒级的精确度
头文件 <time.h>
编译&链接。在编译链接时需加上 -lrt ;因为在librt中实现了clock_gettime函数
函数原型
int clock_gettime(clockid_t clk_id, structtimespect *tp);
参数说明:
clockid_t clk_id 用于指定计时时钟的类型,有以下4种:
CLOCK_REALTIME:系统实时时间,随系统实时时间改变而改变,即从UTC1970-1-10:0:0开始计时,中间时刻如果系统时间被用户该成其他,则对应的时间相应改变
CLOCK_MONOTONIC:从系统启动这一刻起开始计时,不受系统时间被用户改变的影响
CLOCK_PROCESS_CPUTIME_ID:本进程到当前代码系统CPU花费的时间
CLOCK_THREAD_CPUTIME_ID:本线程到当前代码系统CPU花费的时间
struct timespect *tp用来存储当前的时间,其结构如下:
struct timespec
{
time_t tv_sec; /* seconds */
long tv_nsec; /* nanoseconds */
};
返回值。0成功,-1失败
#include<stdio.h>
#include<time.h>
int main()
{
struct timespec ts;
clock_gettime(CLOCK_REALTIME, &ts);
printf("CLOCK_REALTIME: %d, %d", ts.tv_sec, ts.tv_nsec);
clock_gettime(CLOCK_MONOTONIC, &ts);
//打印出来的时间跟 cat /proc/uptime
第一个参数一样
printf("CLOCK_MONOTONIC: %d, %d", ts.tv_sec,ts.tv_nsec);
clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &ts);
printf("CLOCK_PROCESS_CPUTIME_ID: %d, %d", ts.tv_sec,ts.tv_nsec);
clock_gettime(CLOCK_THREAD_CPUTIME_ID, &ts);
printf("CLOCK_THREAD_CPUTIME_ID: %d, %d", ts.tv_sec,ts.tv_nsec);
printf("/n%d/n", time(NULL));
return 0;
}