当前位置: 首页 > 工具软件 > Structr > 使用案例 >

Linux时间相关知识小结:struct timeval、timespec、gettimeofday、time、localtime....

龚威
2023-12-01

前言

我们在linux平台进行开发时,时间相关的操作基本上都会遇到,本文就对常用的时间相关的结构体、接口进行分析小结。

常见类型、结构体定义

timespec

原型

struct timespec
{
  __time_t tv_sec;		/* Seconds. 秒 */
  __syscall_slong_t tv_nsec;	/* Nanoseconds.  纳秒*/
};

说明

该结构体只是包括秒和纳秒,并没有任何其他含义,比如1970年以来的秒数等。只是个存放空间。为clock_gettime的出参。

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

timeval

原型

struct timeval
{
  __time_t tv_sec;		/* Seconds.  秒*/
  __suseconds_t tv_usec;	/* Microseconds.  微秒*/
};

说明

该结构体只是包括秒和微秒,并没有任何其他含义,比如1970年以来的秒数等。只是个存放空间。为gettimeofday的出参。

timezone

原型

struct timezone
  {
    int tz_minuteswest;		/* Minutes west of GMT. 和Greenwich时间差了多少分钟 */
    int tz_dsttime;		/* Nonzero if DST is ever in effect. 日光节约时间的状态  */
  };

说明

该结构体为时区相关信息,用的比较少,gettimeofday的出参。一般我们也可以直接传入NULL.

gettimeofday

原型

int gettimeofday(struct timeval *tv, struct timezone *tz);

说明

获取当前时间,为UTC时间,1970年以来的秒+微秒、时区信息。

参数说明

参数类型说明
tvstruct timeval *出参,当前UTC时间
tzstruct 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

time_t

原型

typedef long time_t;

说明

该类型其实就是long int ,用于存储秒数,是time函数的入参和返回值。

time

原型

time_t time(time_t *t)

说明

该函数会返回从1970年1月1日0时0秒算起到现在所经过的秒数,如果t为非空指针的话,该函数还会将返回值存储到t指针所指的内存里。

参数说明

参数类型说明
ttime_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

tm

原型

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的返回值

localtime

原型

struct tm *localtime(const time_t *timep);

说明

将参数timep所指的当前秒数转换成真实世界所使用的时间日期表示方法,结果返回。

参数说明

参数类型说明
timeptime_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_r

在上面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平台下,与时间相关的常用结构体、变量、接口定义,再详细的说明可以参考相关源码定义。

 类似资料: