(一)前言

说起time,一般有两种,一个是计时,一个是定时.

最近学APUE看到时间和日期这部分,想起muduo书中的实现规则:

1.计时使用gettimeofday(2)来获取当前时间

原因在于比time有更好的精度,比clock_gettime有更小的开销

2.定时使用timerfd_*系列函数来处理定时任务

原因在于可以拿到一个fd放到Reactor中统一管理,并且不像sleep那样产生异步信号,
书中提到可以个用epoll/poll等的timeout参数来指定超时时间,从而实现定时任务,当时并不知道实现原理,
前段时间看Libevent的Timer实现就明白了,在Libevent中,维护一个时间的小根堆,来管理多个超时事件

这又让我想起更早些的一次面试,当时问如何管理定时事件,我说道timerfd,不过面试官好像并不知道这个timerfd的存在,然后把epoll timeout解释给我..

(二)常用时间类型

time_t
struct tm
struct timeval
struct timespec

1.1 time_t

time_t实际是一个长整型:

// time.c
#include <time.h>
#include <stdio.h>
int main()
{
    time_t t;
    return 0;
}

终端输入:
gcc -E time.c | grep __time_t
查看结果:

typedef long int __time_t;
typedef __time_t time_t;

time_t实际是一个长整型。其值表示为从UTC(coordinated universal time)时间1970年1月1日00时00分00秒(也称为Linux系统的Epoch时间)到当前时刻的秒数。

#include <time.h>
time_t time(time_t *calptr);
//将返回当前的时间

1.2 struct tm

struct tm {
    int tm_sec;         /* seconds */
    int tm_min;         /* minutes */
    int tm_hour;        /* hours */
    int tm_mday;        /* day of the month */
    int tm_mon;         /* month */
    int tm_year;        /* year */
    int tm_wday;        /* day of the week */
    int tm_yday;        /* day in the year */
    int tm_isdst;       /* daylight saving time */
};

localtimegmtime将日历时间转换成分解的时间,存放在struct tm

#include <time.h>
struct tm*gmtime(const time_t *calptr);
struct tm*localtime(const time_t *calptr);

gmtime返回格林时间、localtime会返回本地时间,例如返回北京时间

有意思的事情是,time得到是从1970年开始经过的秒数,不过这个结构体中的成员是从1900年算起

只得到这个结构体是无法知道当前确切的时间,通过char *asctime(const struct tm *tm);可以将这个结构体可视化出来:

#include <time.h>
#include <stdio.h>
int main()
{
    time_t t;
    time(&t);
    printf("%ld\r\n",t);
    struct tm* tt=gmtime(&t);//格林时间
    struct tm* localtt=localtime(&t);//北京时间
    printf("gm time: %s\r\n",asctime(tt));
    printf("local time: %s\r\n",asctime(localtt));
    printf("sec: %d\r\n",tt->tm_sec);
    printf("min: %d\r\n",tt->tm_min);
    printf("hour: %d\r\n",tt->tm_hour);
    printf("mday: %d\r\n",tt->tm_mday);
    printf("mon: %d\r\n",tt->tm_mon);
    printf("year: %d\r\n",tt->tm_year);
    printf("wday: %d\r\n",tt->tm_wday);
    printf("yday: %d\r\n",tt->tm_yday);
    return 0;
}

asctime(localtime(&t))的效果就相当于ctime(&t)

1.3 struct timeval

对应的函数是gettimeofday,APUE上说SUSv4指定该函数已经放弃使用,不过我依稀记得我本科毕业设计用它来计算程序运行的时间。
gettimeofday的优势在于可以返回更高的精度(可到微秒级),封装在结构体中:

struct timeval {
    time_t      tv_sec;     /* seconds */
    suseconds_t tv_usec;    /* microseconds */
};

1.4 struct timespec

对应函数

#include <sys/time.h>
int clock_gettime(clockid_t clock_id, struct timespec *tsp);

当ID设置为CLOCK_REALTIME时,clock_gettime将提供与time函数相似的功能,不过在系统支持高精度时间值的情况下,该函数提供纳米级别精度。

struct timespec {
    time_t   tv_sec;        /* seconds */
        long     tv_nsec;       /* nanoseconds */
};

(三)系统调用

上文中提到的系统调用有time()、ctime()、gmtime()、localtime()、asctime()、gettimeofday()

除此之外,用strftmie可以格式化时间输出:

#include <stdio.h>
#include <stdlib.h>
#include <time.h>

int
main(void)
{
    time_t t;
    struct tm *tmp;
    char buf1[16];
    char buf2[64];

    time(&t);
    tmp = localtime(&t);
    if (strftime(buf1, 16, "time and date: %r, %a %b %d, %Y", tmp) == 0)
        printf("buffer length 16 is too small\n");
    else
        printf("%s\n", buf1);
    if (strftime(buf2, 64, "time and date: %r, %a %b %d, %Y", tmp) == 0)
        printf("buffer length 64 is too small\n");
    else
        printf("%s\n", buf2);
    exit(0);
}

(四)参考

1.apue
2.
3.linux多线程编程(陈硕)