文章结构如下:


C支持的日常时间种类

C支持的日常时间格式或种类有这三种:
#####1.时间值`time_t

时间值是指从1970-01-01 00:00:00 +0000 (UTC) 这个时间点开始到当前时间的秒数。
在C语言中,用time_t来表示一个秒数,查看头文件可以发现time_t是对long的一个重定义而已。
相关函数time()
#####2.时间详情 `struct tm

有了具体的时间值time_t,但存在着阅读和理解不方便的问题,所以希望可以把时间值转化为相应的年、月、日、时、分、秒等数据。
对应的结构体为struct tm。如下代码:

1
2
3
4
5
6
7
8
9
10
11
12
struct tm {
int tm_sec; /* 秒,取值范围(0~59),但当遇到闰秒时则会有60秒的取值。 */
int tm_min; /* 分钟数,取值范围(0-59) */
int tm_hour; /* 小时数,取值范围(0-23) */
int tm_mday; /* 当天在这个月中是第几天,取值范围(1-31) */
int tm_mon; /* 当前月份是第几个月,取值范围(0-11) */
int tm_year; /* 从1900年开始至今的年数,即(Year - 1900)的值 */
int tm_wday; /* 当天在本周是第几天,取值范围(0-6, Sunday = 0) */
int tm_yday; /* 当天在今年是第几天,取值范围(0-365, 1 Jan = 0) */
int tm_isdst; /* 夏令时标记,值大于0表示夏令时生效;等于0表示夏令时失效;小于0表示数据不可用。 */
char *tm_zone; /* 时区名称,根据系统不同可能不被声明或不同全名。 */
};

相关函数 localtime() gmtime()

#####3.时间字符串 (char *)

有了年、月、日等具体的数据,C也提供函数将数据转为转为可视的、有意义的字符串。
相关函数

  • 固定格式时间文本:ctime() asctime()
  • 自定义格式时间文本: strftime()

日常时间函数的调用关系

由于涉及到日常时间的函数比较多,这里整理了一张它们的关系图如下:
c.time
好了,先看一眼就好。下面来逐一讲解图中的函数。


日常时间函数的讲解

首先,日常时间函数都需要#include <time.h>

获取当前时间值

time_t time(time_t *t);
time_t mktime(struct tm *tm);
time()可以从当前系统中获取时间值并返回,如果参数t不为NULL,则时间值同样也会存储中t中。
如果函数执行异常,则返回-1,可通过errno查询出错原因。
mktime()则是将struct tm逆解析为time_t的函数。
如果参数tm是由用户自己组装的,则tm中的tm_wdaytm_yday会在本函数执行后根据tm中的tm_year``tm_mon…等字段计算为真实的值。

获取时间详情

时间详情也就是上文所说的struct tm,manual中也把struct tm称为broken-down time。相关函数:

struct tm *gmtime(const time_t *timep);
struct tm *gmtime_r(const time_t *timep, struct tm *result);

struct tm *localtime(const time_t *timep);
struct tm *localtime_r(const time_t *timep, struct tm *result);

gmtime()localtime()都可以把时间值转换为时间详情,两者的区别在于gmtime()所得到的结果是世界标准时间Coordinated Universal Time (UTC);而localtime()获取的是本地时间。以北京时间为例,gmtime()得到的结果比localtime()要提前(早)8小时。

gmtime_r()localtime_r()分别是gmtime()localtime()的可重入实现版本。

当函数执行错误时,则返回NULL,可通过errno查询出错原因。

格式化的时间字符串

char *asctime(const struct tm *tm);
char *asctime_r(const struct tm *tm, char *buf);

char *ctime(const time_t *timep);
char *ctime_r(const time_t *timep, char *buf);

asctime()能直接从时间详情中计算出时间字符串,该字符串包含字符串终止符,计算出来的时间字符串格式是固定的,示例为:Wed Jun 30 21:49:08 1993\n
ctime()则是asctime(localtime(time_t))的封装实现。

asctime_r()ctime_r()分别是asctime()ctime()的可重入实现版本。

  • 注意:以上函数返回的字符串都会包含换行符\n

size_t strftime(char *s, size_t max, const char *format, const struct tm *tm);

C语言为了格式化输出时间信息又提供了强大的格式化函数strftime(),支持按自定义的格式打印出完整可阅读的时间信息,包括月份的全称或简称,或只选择某些关键时间值进行输出。
参数s指向格式化后的时间字符串。
参数max表示时间字符串的最大字符数(含字符串终止符)。
参数format表示时间字符串的格式化表达式。
参数tm表示待格式化的时间详情。

其中format表达式支持格式化参数与字符混合使用,且所支持的格式化参数非常多,具体可以查阅manual。

下面仅挑几个常用的来说:

格式化参数 含义
%Y 完整的年数字
%m 月份,如月份是1~9,则数字前填充一个’0’。
%d 日子
%F 等同于“%Y-%m-%d”
%H 小时数
%M 分钟数
%S 秒数
%_m 月份,如果月份是1~9,则数字前有一个空格填充。
%-m 月份,如果月份是1~9,仍只显示该数字。

示例代码

在这里要多提及一个时间计算函数:

double difftime(time_t time1, time_t time0);

用于计算time1time0之间的时间差,并转换为double,即结果为秒数。

下面是以上提及的所有函数的示例代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
//  author:sodino@qq.com

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

int main (int argc, char ** argv) {
time_t calendar_time = time(NULL);
printf("calendar_time :%ld \n", calendar_time);

char * calendar_str = ctime(&calendar_time);
printf("calendar_str :%s \n", calendar_str);

struct tm * tm_local = localtime(&calendar_time);
printf("localtime :year=%d mon=%d mday=%d hour=%d min=%d sec=%d wday=%d yday=%d isdst=%d \n",
tm_local->tm_year + 1900, tm_local->tm_mon + 1, tm_local->tm_mday, tm_local->tm_hour, tm_local->tm_min, tm_local->tm_sec,
tm_local->tm_wday, tm_local->tm_yday, tm_local->tm_isdst);


char * asc_time = asctime(tm_local);
printf("asc_time :%s", asc_time);
char * c_time = ctime(&calendar_time);
printf("c_time :%s", c_time);

time_t mk_time = mktime(tm_local);
printf("\nmk_time :%ld \n\n", mk_time);


char str_f_t [100];
strftime(str_f_t, sizeof(str_f_t), "%G-%m-%d %H:%M:%S", tm_local);
printf("local :format=%s \n", str_f_t);
struct tm * gm_time = gmtime(&calendar_time);
strftime(str_f_t, sizeof(str_f_t), "%G-%m-%d %H:%M:%S", gm_time);
printf("gmtime :format=%s \n\n", str_f_t);

struct tm my_tm = {2,2,2,16,2,2015,0,0,0};
time_t my_time_t = mktime(&my_tm);
printf("my yday:%d \n", my_tm.tm_yday);

return 0;
}

执行效果:
c.time.output