# 2.1 基础知识
# 2.1.1 时间值
Unix 时间戳,亦称为 Unix 时间、Posix 时间,该值是自协调世界时(Cordinated Universal Time, UTC)1970 年 1 月 1 日 00:00:00 这个特定时间以来所经过的秒数累计值(早期手册称 UTC 为 格林尼治标准时间)。
日历时间包括时间和日期。
系统基本数据类型 time_t
一般是32位或64位整数类型的别名,具体类型取决于当前系统。如果是32位带符号整数,time_t 可以表示的时间到 2038年1月19日03:14:07 UTC 为止;如果是32位无符号整数,则表示到2106年。如果是64位带符号整数,可以表示-2930亿年到+2930亿年的时间范围。
time_t
如果是负数,表示协调世界时之前的时间。
在 shell 中使用 date
命令即可显示当前日期、时间:
$date
2023年 03月 24日 星期五 15:35:33 CST
2
使用 cal
显示日历:
$ cal
三月 2023
日 一 二 三 四 五 六
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
2
3
4
5
6
7
8
unix/linux 和其他操作系统的区别:
- 以协调同一时间而非本地时间计时。
- 可自动进行转换,如变换到夏令时。
- 将时间和日期作为一个量值保存。
# 2.2 API
# 2.2.1 time()
函数原型:
#include <time.h>
time_t time(time_t *tloc);
// tloc: 输出参数,如果非空,时间值也会存放在 *tloc 中。
// 返回值: 成功返回从 1970 年 1 月 1日 00:00:00 起到当前的秒数。
// 失败返回 -1,并设置错误码 errno。
2
3
4
5
6
示例:
#include <time.h>
#include <stdio.h>
int main(void)
{
time_t now = time(NULL);
printf("now: %ld\n",now);
return 0;
}
2
3
4
5
6
7
8
9
10
# 2.2.2 ctime() (已弃用)
用来将 time_t
类型的值直接输出为人类可读的格式。
函数原型:
#include <time.h>
char* ctime(const time_t* timer);
// timer: time_t 指针
// 返回值:类似Www Mmm dd hh:mm:ss yyyy\n\0 的字符串。包含换行符和字符串终止符。
2
3
4
示例:
#include <time.h>
#include <stdio.h>
int main(void)
{
time_t now = time(NULL);
printf("%s",ctime(&now));
return 0;
}
// 输出结果:Fri Mar 24 16:26:22 2023
2
3
4
5
6
7
8
9
10
11
12
# 2.2.3 struct tm
struct tm
是一个用来保存时间的各个组成部分的结构。
定义在 /usr/include/i386-linux-gnu/bits/types/struct_tm.h
中:
/* ISO C `broken-down time' structure. */
struct tm
{
int tm_sec; /* 秒数 [0-60] (1 leap second) */
int tm_min; /* 分钟 [0-59] */
int tm_hour; /* 小时 [0-23] */
int tm_mday; /* 月份的天数 [1-31] */
int tm_mon; /* 月份 [0-11] */
int tm_year; /* 举例 1900 的年数 */
int tm_wday; /* 星期几 [0, 6],星期天 用 0 表示*/
int tm_yday; /* 一年的第几天*/
int tm_isdst; /* 是否采用夏令时,1 采用,0 未采用■[-1/0/1]*/
# ifdef»__USE_MISC
long int tm_gmtoff; /* Seconds east of UTC. */
const char *tm_zone; /* Timezone abbreviation. */
# else
long int __tm_gmtoff; /* Seconds east of UTC. */
const char *__tm_zone; /* Timezone abbreviation. */
# endif
};
// struct timespec 最高精确度是纳秒。
struct timespec {
time_t tv_sec; // 秒数
long tv_nsec; // 纳秒
};
// struct timeval最高精确度是微秒。
struct timeval {
time_t tv_sec; // seconds
long tv_usec; // microseconds
};
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
秒可以超过 59 的理由是可以表示闰秒。除了 tm_mday
其他字段都以 0 开始。
# 2.2.4 localtime(),gmtime()
这两个函数可以将 time_t
类型的时间 转换成 struct tm
结构。
函数原型:
#include <time.h>
struct tm *gmtime(const time_t *timer);
// timer: 指向要转换的 time_t 对象
// 返回值: 成功返回一个指向 struct tm 结构的指针,其他返回 NULL。
struct tm *localtime (const time_t *timer);
// timer: 指向要转换的 time_t 对象
// 返回值: 成功指向 struct tm 结构的指针,失败返回 NULL。
2
3
4
5
6
7
8
localtime()
和 gmtime()
的区别:
gmtime()
将 time_t
得到的秒数转换成一个 协调统一时间(UTC 时间)的 struct tm
结构体。
localtime()
是将时区考虑在内了,转出的当前时区的本地时间。
# 2.2.5 asctime() (已弃用)
用来将 struct tm
结构,直接输出为人类可读的格式。
函数原型:
#include <time.h>
char *asctime(const struct tm *tm);
// tm: struct tm 对象
// 返回值:输出人类可读的格式,自动在行尾添加换行符,失败返回 NULL。
2
3
4
示例:
int main(int argc, const char* argv[])
{
time_t now = time(NULL);
printf("%s",ctime(&now));
struct tm *t = gmtime(&now);
struct tm *y = localtime(&now);
printf("%d:%d:%d\n",t->tm_hour,t->tm_min,t->tm_sec);
printf("%s",asctime(t));
printf("%d:%d:%d\n",y->tm_hour,y->tm_min,y->tm_sec);
printf("%s",asctime(y));
return EXIT_SUCCESS;
}
// 运行结果:
Sun Mar 26 15:14:39 2023
15:14:39
Sun Mar 26 15:14:39 2023
15:14:39
Sun Mar 26 15:14:39 2023
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 2.2.6 mktime()
mktime()
函数用于把一个 struct tm
结构转换为 time_t
值。
函数原型:
time_t mktime(struct tm *tm);
// tm: struct tm 对象
// 返回值:time_t 时间值,失败返回 -1,并设置错误码
2
3
# 2.2.7 strftime()
类似 printf()
,通过可用的多个参数来定制产生的字符串。
size_t strftime( char *restrict str, size_t count,
const char *restrict format, const struct tm *restrict time );
// str: 目标字符串的指针
// count: 目标字符串可以接受的最大长度
// format: 格式字符串
// time: struct tm 结构
// 返回值: 若有空间,返回存入数组的字符数,否则返回 0。
2
3
4
5
6
7
37 种 ISO C 规定的转换说明:
使用示例:
int main(int argc, const char* argv[])
{
time_t t;
struct tm *tmp;
char buf1[16];
char buf2[64];
time(&t);
tmp = localtime(&t);
if (strftime(buf1, sizeof(buf1), "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, sizeof(buf2), "time and date: %r, %a %b %d, %Y",tmp) == 0)
printf("buffer length 64 is too small\n");
else
printf("%s\n",buf2);
return EXIT_SUCCESS;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
运行结果:
$ ./a.out
buffer length 16 is too small
time and date: 03:35:26 PM, Sun Mar 26, 2023
2
3
# 2.2.8 clock()
clock() 函数返回从程序开始执行到当前的 CPU 时钟周期。一个时钟周期等于 CPU 频率的倒数,比如 CPU 的频率如果是 1G Hz,表示1秒内时钟信号可以变化 109 次,那么每个时钟周期就是 10-9 秒。
clock_t clock(void);
// 返回值:成功返回 cpu 时间周期
// 失败返回 -1
2
3
为了把这个值转换为秒,应该把它除以常量 CLOCKS_PER_SEC
(每秒的时钟周期),这个常量也由time.h定义。
示例:
clock_t start = clock();
// 执行某些操作
clock_t end = clock();
long double seconds = (float)(end - start) / CLOCKS_PER_SEC;
2
3
4
5
# 2.2.9 difftime()
用来计算两个时间之间的差异。Unix 系统上,直接相减两个 time_t 值,就可以得到相差的秒数,但是为了程序的可移植性,最好还是使用这个函数。
double difftime( time_t time1, time_t time2 );
difftime()函数接受两个 time_t 类型的时间作为参数,计算 time1 - time2 的差,并把结果转换为秒。
注意它的返回值是 double 类型。
使用示例:
#include <stdio.h>
#include <time.h>
int main(void) {
struct tm time_a = {
.tm_year=82,
.tm_mon=3,
.tm_mday=12,
.tm_hour=4,
.tm_min=00,
.tm_sec=04,
.tm_isdst=-1,
};
struct tm time_b = {
.tm_year=120,
.tm_mon=10,
.tm_mday=15,
.tm_hour=16,
.tm_min=27,
.tm_sec=00,
.tm_isdst=-1,
};
time_t cal_a = mktime(&time_a);
time_t cal_b = mktime(&time_b);
double diff = difftime(cal_b, cal_a);
double years = diff / 60 / 60 / 24 / 365.2425;
// 输出 1217996816.000000 seconds (38.596783 years) between events
printf("%f seconds (%f years) between events\n", diff, years);
}
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
上面示例中,折算年份时,为了尽量准确,使用了一年的准确长度 365.2425 天,这样可以抵消闰年的影响。
各个时间函数的关系: