time 函数可用于返回当前的时间和日期。
#includetime_t time(time_t *calptr); /* 返回值:若成功,返回时间值;否则,返回 -1 */
时间值作为函数值返回。如果参数非空,则时间值也存放在由 calptr 指向的单元内。
POSXI.1 的实时扩展增加了对多个系统时钟的支持。时钟通过 clockid_t 类型进行标识,标准的值如下表所示。
下面几个函数可以用来操作时钟。
#includeint clock_gettime(clockid_t clock_id, struct timespec *tsp); int clock_getres(clockid_t clock_id, struct timespec *tsp); int clock_settime(clockid_t clock_id, const struct timespec *tsp); /* 返回值:若成功,都返回 0;否则,都返回 -1 */ int gettimeofday(struct timeval *restrict tp, void *restrict tzp); /* 返回值:总是返回 0 */ struct timespec{ time_t tv_sec; long tv_nsec; }; struct timeval{ time_t tv_sec; // seconds long tv_usec; // microseconds };
clock_gettime 函数可用于获取指定时钟的时间,返回的时间在 timespec 结构中。当 clock_id 设置为 CLOCK_REALTIME 时,clock_gettime 就提供了类似 time 的功能,只是可能拥有更高的精度。
clock_getres 函数把参数 tsp 指向的 timespec 结构初始化为与 clock_id 参数对应的时钟精度。例如,如果精度为 1 毫秒,则 tv_sec 字段就是 0,tv_nsec 字段就是 1000000。
clock_settime 函数可对特定的时钟设置时间,这需要适当的特权,而且有些时钟是不能修改的(历史上,System V 派生的系统是调用 stime 函数来设置系统时间,而 BSD 派生的系统是调用 settimeofday 函数来设置系统时间)。
gettimeofday 函数已被 SUSv4 指定为弃用,但一些程序仍然在使用,因为它提供了比 time 函数更高的精度(可到微秒级)。tzp 参数的唯一合法值是 NULL,其他值将产生不确定的结果。该函数将当前时间秒数存放在 tp 指向的 timeval 结构中,而该结构将当前时间表示为秒和微秒。
一旦取得这种从上述特定时间经过的秒数的整型时间值后,通常需要调用函数将其转换为分解的时间结构,然后调用另一个函数生成可读的时间和日期。
#includestruct tm *gmtime(const time_t *calptr); struct tm *localtime(const time_t *calptr); /* 返回值:若成功,都返回指向分解的 tm 结构的指针;否则,都返回 NULL */ time_t mktime(struct tm *tmptr); /* 返回值:若成功,返回日历时间;否则,返回 -1 */ size_t strftime(char *restrict buf, size_t maxsize, const char *restrict format, const struct tm *restrict tmptr); size_t strftime_l(char *restrict buf, size_t maxsize, const char *restrict format, const struct tm *restrict tmptr, locale_t locale); /* 返回值:若有空间,都返回存入数组的字符数;否则,都返回 0 */ char *strptime(const char *restrict buf, const char *restrict format, struct tm *restrict tmptr); /* 返回值:指向上次解析的字符的下一个字符的指针;否则,返回 NULL */ struct tm{ // a broken-down time int tm_sec; // seconds after the minute: [0 - 60] int tm_min; // minutes after the hour: [0 - 59] int tm_hour; // hours after midnight: [0 - 23] int tm_mday; // day of the month: [1 - 31] int tm_mon; // months since January: [0 - 11] int tm_year; // years since 1900 int tm_wday; // days since Sunday: [0 - 6] int tm_yday; // days since January: [0 - 365] int tm_isdst; // daylight saving time flag: <0, 0, >0 };
这里的秒可以超过 59 的理由是可以表示润秒(Single UNIX Specification 的以前版本允许双润秒,所以 tm_sec 的有效范围是 0~61)。除了月日字段,其他字段的值都以 0 开始。如果夏令时生效,则夏令时标志为正;如果为非夏令时时间,则该标志值为 0;如果此信息不可用,则其值为负。
localtime 和 gmtime 函数将日历时间转换成分解的时间,并存放在 tm 结构中。它们的区别是:localtime 转换成本地时间,而 gmtime 转换成协调统一时间。
mktime 函数将本地时间转换成 time_t 值。
strftime 函数可通过可用的多个参数来定制产生的字符串。strftime_l 允许将区域指定为参数,strftime 使用通过 TZ 环境变量指定的区域。除此之外,strftime 和 strftime_l 是相同的(两个较早的函数 asctime 和 ctime 能用于产生一个 26 字节的可打印的字符串,类似于 date 命令的默认输出,但它们已因缓冲区溢出问题而被标记为弃用了)。
tmptr 参数是要格式化的时间值。格式化结果存在一个长度为 maxsize 个字符的 buf 数组中。
format 参数控制时间值的格式,其中除转换说明以外的字符都按原样输出。以下列出了 37 种 ISO C 规定的转换说明。
其中可能需要略作解释的是 %U、%V 和 %W。%U 是相应日期在该年中所属周数,包含该年中第一个星期日的周是第一周。%W 也是相应日期在该年中所属的周数,不同的是包含第一个星期一的周为第一周。%V 说明符则是如果包含了 1 月 1 日的那一周包含了新一年的 4 天以上,那么该周是一年中的第一周;否则就被认为是上一年的最后一周。在这两种情况下,周一都被视作每周的第一天。
strftime 同 printf 对某些转换说明支持修饰符,可以使用 E 和 O 修饰符产生本地支持的另一种格式。
strptime 函数是 strftime 的相反版本,把字符串时间转换成分解时间。format 参数给出了 buf 参数指向的缓冲区内的字符串的格式。
下面这个程序简单演示了 strftime 函数的使用。
#include#include #include 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); }
运行结果如下。
$ ./strftimeDemo.out buffer length 16 is too small time and date: 00:45:35 AM, Fri Aug 19, 2017
下图说明了各种时间函数之间的关系。
图中的 localtime、mktime 和 strftime 三个函数都受到环境变量 TZ 的影响。如果定义了 TZ,则这些函数将使用其值代替默认时区。如果定义为空串(即 TZ=),则使用 UTC。TZ 的值常常类似于 TZ=EST5EDT。