我们在linux平台进行开发时,时间相关的操作基本上都会遇到,本文就对常用的时间相关的结构体、接口进行分析小结。
原型
struct timespec
{
__time_t tv_sec; /* Seconds. 秒 */
__syscall_slong_t tv_nsec; /* Nanoseconds. 纳秒*/
};
说明
该结构体只是包括秒和纳秒,并没有任何其他含义,比如1970年以来的秒数等。只是个存放空间。为clock_gettime的出参。
原型
int clock_gettime (clockid_t __clock_id, struct timespec *__tp);
说明
根据clock_id(系统时钟)的类型,获取当前时间。
参数说明
clock_id常用取值枚举说明
枚举值 | 说明 |
---|---|
CLOCK_REALTIME | 系统当前时间,从1970年1月1日算起 |
CLOCK_MONOTONIC | 从系统启动时间算起,经过的时间 |
CLOCK_PROCESS_CPUTIME_ID | 本进程运行时间 |
CLOCK_THREAD_CPUTIME_ID | 本线程运行的时间 |
程序示例
#include <time.h>
#include <stdio.h>
#include <sys/time.h>
int main(void)
{
struct timespec tsp;
printf("CLOCK REALTIME time:\n");
clock_gettime(CLOCK_REALTIME, &tsp);
printf("sec:%ld, nsec:%ld\n", tsp.tv_sec, tsp.tv_nsec);
printf("-----------------------\n");
printf("CLOCK MONOTONIC time:\n");
clock_gettime(CLOCK_MONOTONIC, &tsp);
printf("sec:%ld, nsec:%ld\n", tsp.tv_sec, tsp.tv_nsec);
printf("-----------------------\n");
printf("CLOCK PROCESS CPUTIME ID time:\n");
clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &tsp);
printf("sec:%ld, nsec:%ld\n", tsp.tv_sec, tsp.tv_nsec);
printf("-----------------------\n");
printf("CLOCK THREAD CPUTIME ID time:\n");
clock_gettime(CLOCK_THREAD_CPUTIME_ID, &tsp);
printf("sec:%ld, nsec:%ld\n", tsp.tv_sec, tsp.tv_nsec);
printf("-----------------------\n");
return 0;
}
运行结果:
CLOCK REALTIME time:
sec:1644155596, nsec:748701786
-----------------------
CLOCK MONOTONIC time:
sec:12526311, nsec:817330221
-----------------------
CLOCK PROCESS CPUTIME ID time:
sec:0, nsec:585929
-----------------------
CLOCK THREAD CPUTIME ID time:
sec:0, nsec:590570
原型
struct timeval
{
__time_t tv_sec; /* Seconds. 秒*/
__suseconds_t tv_usec; /* Microseconds. 微秒*/
};
说明
该结构体只是包括秒和微秒,并没有任何其他含义,比如1970年以来的秒数等。只是个存放空间。为gettimeofday的出参。
原型
struct timezone
{
int tz_minuteswest; /* Minutes west of GMT. 和Greenwich时间差了多少分钟 */
int tz_dsttime; /* Nonzero if DST is ever in effect. 日光节约时间的状态 */
};
说明
该结构体为时区相关信息,用的比较少,gettimeofday的出参。一般我们也可以直接传入NULL.
原型
int gettimeofday(struct timeval *tv, struct timezone *tz);
说明
获取当前时间,为UTC时间,1970年以来的秒+微秒、时区信息。
参数说明
参数 | 类型 | 说明 |
---|---|---|
tv | struct timeval * | 出参,当前UTC时间 |
tz | struct timezone * | 当前时区信息 |
返回值
成功则返回0,失败则返回-1,错误原因存在errno中。
程序示例
#include <time.h>
#include <stdio.h>
#include <sys/time.h>
int main(void)
{
struct timeval tv;
struct timezone tz;
printf("gettimeofday time:\n");
gettimeofday(&tv, &tz);
printf("tv_sec:%ld tv_usec:%ld\n", tv.tv_sec, tv.tv_usec);
printf("tz:%d %d\n", tz.tz_dsttime, tz.tz_minuteswest);
return 0;
}
运行结果:
gettimeofday time:
tv_sec:1644156383 tv_usec:513679
tz:0 0
原型
typedef long time_t;
说明
该类型其实就是long int ,用于存储秒数,是time函数的入参和返回值。
原型
time_t time(time_t *t)
说明
该函数会返回从1970年1月1日0时0秒算起到现在所经过的秒数,如果t为非空指针的话,该函数还会将返回值存储到t指针所指的内存里。
参数说明
参数 | 类型 | 说明 |
---|---|---|
t | time_t * | 返回的秒存储指针 |
返回值
成功则返回秒数,失败则返回-1,错误原因存在errno中。
程序示例
#include <time.h>
#include <stdio.h>
#include <sys/time.h>
int main(void)
{
time_t timep1, timep2;
timep1 = time(NULL);
time(&timep2);
printf("timep1:%ld timep2:%ld\n", timep1, timep2);
return 0;
}
运行结果:
timep1:1644157011 timep2:1644157011
原型
struct tm
{
int tm_sec; /* Seconds. [0-60] (1 leap second) */
int tm_min; /* Minutes. [0-59] */
int tm_hour; /* Hours. [0-23] */
int tm_mday; /* Day. [1-31] */
int tm_mon; /* Month. [0-11] 注意:0代表1月,以此类推*/
int tm_year; /* Year - 1900. 该值为实际年份减去1900*/
int tm_wday; /* Day of week. [0-6] 注意:0代表星期一,以此类推*/
int tm_yday; /* Days in year.[0-365] 从每年的1月1日开始的天数,其中0代表1月1日,以此类推*/
int tm_isdst; /* DST. [-1/0/1] 夏玲时标识符*/
};
说明
该类型用于描述真实世界所使用的时间日期,包括年月日时分秒周等, 是localtime的返回值
原型
struct tm *localtime(const time_t *timep);
说明
将参数timep所指的当前秒数转换成真实世界所使用的时间日期表示方法,结果返回。
参数说明
参数 | 类型 | 说明 |
---|---|---|
timep | time_t | 当前UTC秒数 |
返回值
当前UTC秒数对应的年月日时分秒周等。
程序示例
#include <time.h>
#include <stdio.h>
#include <sys/time.h>
int main(void)
{
time_t timep;
struct tm *p;
timep = time(NULL);
printf("timep:%ld\n", timep);
p = localtime(&timep);
// 对年、月要进行相应的处理
printf("%d-%d-%d %d:%d:%d \n", 1900 + p->tm_year, 1 + p->tm_mon, p->tm_mday,
p->tm_hour, p->tm_min, p->tm_sec);
return 0;
}
运行结果:
timep:1644157741
2022-2-6 22:29:1
在上面localtime的程序示例中,可以发现,localtime的返回值是 struct tm *类型,也就是指向struct tm类型的指针,这样使用并不会造成内存泄露等问题,这是因为localtime的返回值其实指向了一个静态变量,这是静态库已经定义的static struct tm类型数据,很明显,由于程序使用静态变量,会导致localtime不可重入,也就是非现成安全,对于一般的应用场景并不会带来什么隐患,但是对于实时性要求高的场所,会带来问题,因为多次调用localtime会造成返回值覆盖。
比如:
两个线程A和B同时调用 localtime 函数:
时刻1:线程A调用 localtime 函数,得到一个指针,tm中存储的值更新为 value-A
时刻2:线程B调用 localtime 函数,得到一个指针, tm中存储的值更新为 value-B
时刻3:线程A对 localtime 返回的指针进行相关引用操作(例如 printf 输出某字段),此时 static struct tm 中的值实际是 value-b,并非预期的 value-a
时刻4:线程B对 localtime 返回的指针进行相关引用操作,此时 static struct tm 中的值实际是 value-b
所以为了解决localtime不可重入的问题,就出现了localtime_r。
原型
struct tm *localtime_r(const time_t *timep, struct tm *result);
说明
调用 localtime 只需要传入指向 time_t 的一个常量指针;
调用 localtime_t 不仅需要传入指向 time_t 的一个常量指针,还需要传入指向 struct tm 的一个指针,结果将存储在 result 指向的 struct tm 对象中;
程序示例
#include <time.h>
#include <stdio.h>
#include <sys/time.h>
int main(void)
{
time_t timep;
struct tm *p;
struct tm res;
timep = time(NULL);
printf("timep:%ld\n", timep);
localtime_r(&timep, &res);
printf("%d-%d-%d %d:%d:%d \n", 1900 + res.tm_year, 1 + res.tm_mon, res.tm_mday,
res.tm_hour, res.tm_min, res.tm_sec);
return 0;
}
运行结果:
timep:1644158806
2022-2-6 22:46:46
以上为linux平台下,与时间相关的常用结构体、变量、接口定义,再详细的说明可以参考相关源码定义。