在应用程序开发过程中,经常要使用到日期时间这些参数,Linux系统中提供了相关的一些API给用户获取日期时间,这些API都放在头文件time.h中。
此外,需要注意的是,所有的UNIX系统都使用同一个时间和日期的起点:格林尼治时间(GMT)1970年1月1日午夜(0点)。这是“UNIX纪元的起点”,Linux也不例外。Linux系统中所有的时间都以从UNIX纪元的起点起经过的秒数来衡量。
接下来,我们来介绍日期时间相关的API及其使用。
前面说过Linux系统中所有的时间都以从UNIX纪元的起点起经过的秒数来衡量,该秒数称为底层的时间值。
1、time函数
#include
time_t time(time_t *tloc);
作用:通过调用time函数得到底层的时间值,它返回的是从纪元开始至今的秒数。
参数:如果tloc不是一个空指针,time函数还会把返回值写入tloc指针指向的位置。
返回值:底层的时间值,单位是秒。
也就是说获取底层时间值可以通过参数获取,也可以通过返回值获取。
时间通过一个预定义的类型time_t来处理。这是一个大到能够容纳以秒计算的日期和时间的整数类型。
2、difftime函数
既然可以通过time函数获取底层的时间值,那么对于一些精度要求不高的事情的耗时统计,可以通过简单地把两次调用time函数得到的值相减。此外,Linux还提供了difftime函数来计算两个time_t时间值的差,且精度更高。
#include
double difftime(time_t time1, time_t time2);
该函数用来计算两个time_t值之间的秒数并以double类型返回它。
作用:计算两个时间值之间的差,
参数:两个time_t底层时间值。
返回值:将time1-time2的值作为浮点数返回。
示例代码:
创建time.c文件,加入如下代码:
#include
#include
#include
#include
int main(int argc, char *argv)
{
time_t begin_time, end_time;
double diff_time;
begin_time = time(NULL);
sleep(1);
time(&end_time);
diff_time = difftime(end_time, begin_time);
printf("begin time: %lds, end time: %lds, diff time: %fs\n",
begin_time, end_time, diff_time);
exit(0);
}
编译:gcc -o time time.c
运行:
为了提供更有意义的时间和日期,你需要把时间值转换为可读的时间和日期。有一些标准函数可以帮我们做到这一点。
1、gmtime函数
#include
struct tm *gmtime(const time_t *timeval);
作用:gmtime函数把底层时间值分解为一个结构。
参数:time_t底层时间值。
返回值:tm结构体时间。tm结构体有如下成员:
注意:tm_year是从1900年开始计算的偏移值,该值要加1900才是当年的值。tm_mon是从0开始的值,该值要加1才是当前的月份。
2、localtime函数
由于gmtime函数获得的是GMT时间(格林尼治时间,或称为时间标准时间UTC),GMT时间是时区0的时间,不一定是用户想要的时区时间,如果要获取当地时间,就要失业localtime函数。
#include
struct tm *localtime(const time_t *timeval);
localtime函数和gmtime一样,区别是它返回的结构中包含的值已根据当地时区做了调整。
3、mktime函数
如果已知tm结构时间,想得到底层时间time_t,可以使用mktime函数转换。
#include
time_t mktime(struct tm *timeptr);
作用:把已分解出来的tm结构再转换为底层时间值time_t。
参数:tm结构体指针。
返回值:底层时间time_t。如果tm结构不能被表示为time_t值,mktime将返回-1。
示例代码:
创建tmtime.c文件,加入如下代码:
#include
#include
#include
#include
int main(int argc, char *argv)
{
time_t time_val, time_mk;
struct tm *time_gm, *time_local;
time_val = time(NULL);
printf("raw second time: %lds\n", time_val); //获取底层时间值
time_gm = gmtime(&time_val);
printf("raw gm time:\n"); //打印原始的tm结构时间
printf("\tdate:%03d/%02d/%02d, time: %02d:%02d:%02d\n",
time_gm->tm_year, time_gm->tm_mon, time_gm->tm_mday,
time_gm->tm_hour, time_gm->tm_min, time_gm->tm_sec);
printf("modified gm time:\n"); //打印修改后的tm结构时间
printf("\tdate:%04d/%02d/%02d, time: %02d:%02d:%02d\n",
time_gm->tm_year+1900, time_gm->tm_mon+1, time_gm->tm_mday,
time_gm->tm_hour, time_gm->tm_min, time_gm->tm_sec);
time_local = localtime(&time_val);
printf("modified local time:\n"); //打印本地时间
printf("\tdate:%04d/%02d/%02d, time: %02d:%02d:%02d\n",
time_gm->tm_year+1900, time_gm->tm_mon+1, time_gm->tm_mday,
time_gm->tm_hour, time_gm->tm_min, time_gm->tm_sec);
time_mk = mktime(time_gm); //将tm结构时间转换为底层时间值
if(time_mk<0)
{
printf("mktime error!\n");
exit(1);
}
printf("raw second mktime: %lds\n", time_mk);
exit(0);
}
编译:gcc -o tmtime tmtime.c
运行:
为了更方便的输出日期和时间的表示,可以使用asctime函数和ctime函数输出固定格式表示的日期时间。
1、asctime函数
#include
char *asctime(const struct tm *timeptr);
参数:tm结构体指针。
返回值:字符串日期时间。表示由tm结构timeptr所给出的时间和日期。这个返回的字符串有类似下面的格式:
Sun Jun 9 12:34:56 2019\n\0
该字符串的长度是固定26个字符。表示的是GMT时间的字符串。
2、ctime函数
如果要表示本地时间的字符串,则要使用ctime函数。
#include
char *ctime(const time_t *timeval);
作用:获取本地的字符串格式日期时间。
参数:底层时间值time_t。
返回值:本地日期时间的字符串。
ctime函数和asctime一样,区别是它返回的字符串中包含的值已根据当地时区做了调整。
示例代码:
创建strtime.c文件,加入如下代码:
#include
#include
#include
#include
int main(int argc, char *argv)
{
time_t time_val;
struct tm *time_gm;
char *time_asc, *time_c;
time_val = time(NULL);
printf("raw second time: %lds\n", time_val); //获取底层时间值
time_gm = gmtime(&time_val); //获取tm结构时间
time_asc = asctime(time_gm); //将tm结构时间转换为GMT字符串
printf("asctime:\n");
printf("%s\n", time_asc);
time_c = ctime(&time_val); //将底层时间值转换为本地时间字符串
printf("ctime:\n");
printf("%s\n", time_c);
exit(0);
}
编译:gcc -o strtime strtime.c
运行:
为了对时间和日期字符串的格式有更多控制,可以使用strftime函数,它很像是一个针对时间和日期的sprintf函数,工作方式也很类似。
1、strftime函数
#include
size_t strftime(char *s, size_t maxsize, const char *format, struct tm *timeptr);
作用:创建一个自定义格式日期时间字符串。
参数:timeptr指针指向的tm结构所表示的时间和日期,并将结果放在字符串s中。字符串被指定(至
少)maxsize个字符长。format字符串用于控制写入字符串s的字符。与printf一样,它包含将被传给字符串的普通字符和用于格式化时间和日期元素的转换控制符。转换控制符如下表:
2、strptime函数
如果已知一个包含日期时间的字符串,可以使用strptime函数从该字符串中解析日期时间。
#include
char *strptime(const char *buf, const char *format, struct tm *timeptr);
参数:buf是存放一个已知的字符串,其格式与format字符串一致。format字符串的构建方式和strftime的format字符串完全一样。strptime在字符串扫描方面类似于sscanf函数,也是查找可识别字段,并把它们写入tm结构体指针timeptr中对应的变量。
返回值:strptime返回一个指针,指向转换过程处理的最后一个字符后面的那个字符。如果碰到不能转换的字符,转换过程就在该处停下来。调用程序需要检查是否已从传递的字符串中读入了足够多的数据,以确保tm结构中写入了有意义的值。
strptime的转换控制符与strftime的相比,限制要稍微松一些,因为strptime中的星期几和月份用缩写和全称都行,两者都匹配strptime中的%a控制符,此外,strftime对小于10的数字总以0开头,而strptime则把它看作是可选的。
示例代码:
创建formtime.c文件,添加如下代码:
#define _XOPEN_SOURCE
#include
#include
#include
#include
#include
int main(int argc, char *argv)
{
time_t time_val;
struct tm *time_local;
struct tm tm_strp;
char time_buf[256] = {0};
char *result = NULL;
time_val = time(NULL);
time_local = localtime(&time_val);
strftime(time_buf, sizeof(time_buf), "%a %d %b %H:%M:%S", time_local);
printf("strftime:\n");
printf("%s\n", time_buf);
memset(time_buf, 0, sizeof(time_buf));
strcpy(time_buf, "Thu 29 July 2020, 18:25 will finish");
printf("strptime will get msg from: %s\n", time_buf);
result = strptime(time_buf, "%a %d %b %Y, %H:%M", &tm_strp);
printf("strptime:\n");
printf("date:%04d/%02d/%02d, time: %02d:%02d:%02d\n",
tm_strp.tm_year+1900, tm_strp.tm_mon+1, tm_strp.tm_mday,
tm_strp.tm_hour, tm_strp.tm_min, tm_strp.tm_sec);
exit(0);
}
编译:gcc -o formtime formtime.c
运行:
注意:编译formtime.c时,你可能会看到编译器有一个警告信息。这是因为GNU库在默认情况下并未声明strptime函数。要解决这个问题,你需要明确请求使用X/Open的标准功能,这需要在包含time.h头文件之前加上如下一行:
#define _XOPEN_SOURCE
关于time_t时间值、tm结构体和格式化字符串之间的转换关系如下图:
参考:
《Linux程序设计第4版》