目录
1 了解Linux系统时间
1.1 几种常用的时间
1.2 如何查看几种常用的时间
1.3 Linux 系统中的时间
2 time、gettimeofday获取时间
2.1 time函数
2.2 gettimeofday函数:
2.3 示例程序
3 时间转换函数
3.1 ctime与ctime_r函数
3.2 localtime与localtime_r函数
3.3 gmtime和gmtime_r函数
3.4 mktime()函数
3.5 asctime 与asctime_r函数
3.6 strftime 函数
3.7 综合示例
4 设置时间 settimeofday
1 了解Linux系统时间
1.1 几种常用的时间
在正式介绍这些时间、日期相关的系统调用或 C 库函数之前,需要向大家介绍一些时间相关的基本概念,譬如 GMT 时间、 UTC 时间以及时区等。
GMT时间:GMT是格林尼治平均时间,以地球的本初子午线——通过英国伦敦郊区的格林尼治天文台的经线——为基准。GMT是历史上用来确定时间的标准,它不随地球自转速度的变化而调整。
UTC时间:目前国际上用来替代GMT的官方时间标准。UTC使用高精度的原子钟来测量时间,并且可以添加闰秒以保持与地球自转周期的一致性。UTC的主要目的是为了提供一个稳定的时间标准,不受地球自转速度微小变化的影响。
地区时间:实际,世界上不少国家和地区都不严格按时区来计算时间。为了在全国范围内采用统一的时间,一般都把某一个时区的时间作为全国统一采用的时间。例如,我国把首都北京所在的东 8 区的时间作为全国统一的时间,称为北京时间, 北京时间就作为我国使用的本地时间, 譬如我们电脑上显示的时间就是北京时间。
1.2 如何查看几种常用的时间
在 Ubuntu 系统下,可以使用 date 命令查看系统当前的本地时间,可以看到显示出来的字符串后面有一个"CST"字样, CST 在这里其实指的是 China Standard Time(中国标准时间)的缩写,如下所示:
要显示UTC时间,可以使用date -u
或者date --utc
1.3 Linux 系统中的时间
"系统时钟"和"实时时钟"
在计算机系统中,"系统时钟"(System Clock)和"实时时钟"(Real Time Clock, RTC)是两种不同类型的时钟,它们有不同的用途和特性:
系统时钟(System Clock):
- 系统时钟是计算机的主时钟,用于跟踪当前的日期和时间。它通常由操作系统管理,并且可以受到用户设置和网络时间协议(NTP)的影响。
- 系统时钟可能会因为多种原因(如系统休眠或时间同步)而调整或变化。
实时时钟(Real Time Clock, RTC):
- 实时时钟是一种独立于主系统的时钟,通常由一个小型电池供电,即使在计算机关机时也能保持时间。RTC用于在系统启动时设置系统时钟,并在系统运行时提供准确的时间参考。
- RTC通常具有较低的精度,但能够在没有外部电源的情况下保持时间,使其成为记录系统启动时间和维护系统时间连续性的关键组件。
jiffies 的引入
Jiffies是Linux内核中用于时间度量的一个概念,它是一个自系统启动以来的计时器,以固定频率递增。每个jiffy的长度取决于系统的时钟频率,通常在不同系统上会有所不同,但大约相当于几毫秒。Jiffies被用来测量系统的运行时间、调度任务和执行时间相关的计算。由于jiffies与实际时间的秒数不是固定比例,它主要用于内核内部的时间管理,而不是用来获取精确的日期和时间。
2 time、gettimeofday获取时间
time
和 gettimeofday
是两个在Linux系统中用于获取时间的函数,分别定义在 <time.h>
和 <sys/time.h>
头文件中。
2.1 time函数
time
函数用于获取自1970年1月1日(UTC)以来经过的秒数,函数原型:
#include <time.h>
time_t time(time_t *t);
- 如果
t
参数不为NULL
,time
函数还会将当前时间戳复制到t
指向的位置。 - 返回值是当前时间的时间戳,如果出现错误,则返回
-1
并设置errno
。
2.2 gettimeofday函数:
time()获取到的时间只能精确到秒,如果想要获取更加精确的时间可以使用系统调用 gettimeofday 来实现, gettimeofday()函数提供微秒级时间精度,函数原型如下所示:
#include <sys/time.h>
int gettimeofday(struct timeval *tv, struct timezone *tz);
- 参数
tv
指向一个timeval
结构,该结构将被填充当前时间的秒和微秒。 - 参数
tz
指向一个timezone
结构,如果提供,它将被填充关于本地时区的信息。
2.3 示例程序
以下是使用 time
和 gettimeofday
函数的示例:
#include <stdio.h>
#include <time.h>
#include <sys/time.h>
int main() {
// 使用 time 函数获取当前时间戳
time_t current_time = time(NULL);
printf("Current UNIX timestamp: %ld\n", current_time);
// 使用 gettimeofday 函数获取当前的日期和时间
struct timeval now;
if (gettimeofday(&now, NULL) == 0) {
printf("Current date and time: %ld.%06ld\n", now.tv_sec, (long)now.tv_usec);
} else {
perror("gettimeofday failed");
}
return 0;
}
运行结果如下:
3 时间转换函数
通过 time()或 gettimeofday()函数可以获取到当前时间点相对于 1970-01-01 00:00:00 +0000(UTC)这个时间点所经过时间(日历时间) ,所以获取得到的是一个时间段的长度,但是这并不利于我们查看当前时间,下面介绍一些系统调用或 C 库函数,通过这些 API 可以将 time()或gettimeofday()函数获取到的秒数转换为利于查看和理解的形式。
3.1 ctime与ctime_r函数
ctime()是一个 C 库函数, 可以将日历时间转换为可打印输出的字符串形式, ctime()函数原型如下所示:
#include <time.h>
char *ctime(const time_t *timep);
char *ctime_r(const time_t *timep, char *buf);
ctime
函数:
将时间戳(以time_t
类型表示)转换为一个以null结尾的字符串,格式为"Thu Jan 01 00:00:00 1970\n\0"。这个函数不是线程安全的,因为它只返回一个静态缓冲区的指针。ctime_r
函数:
与ctime
功能相同,但它是线程安全的,因为它将结果写入由调用者提供的缓冲区buf
,推荐大家使用可重入函数 ctime_r()。
3.2 localtime与localtime_r函数
localtime()函数可以把 time()或 gettimeofday()得到的秒数(time_t 时间或日历时间) 变成一个 struct tm结构体所表示的时间, 该时间对应的是本地时间。 localtime 函数原型如下:
#include <time.h>
struct tm *localtime(const time_t *timep);
struct tm *localtime_r(const time_t *timep, struct tm *result);
- localtime函数:返回一个指向静态分配的
struct tm
的指针,该结构包含了转换后的本地时间。意味着每次调用localtime
都会覆盖上一次调用的结果,因此它不是线程安全的。 - localtime_r函数:接受两个参数,第一个是指向
time_t
的指针,第二个是指向由调用者提供的struct tm
结构的指针。转换的结果将直接存储在result
指向的结构中。由于结果存储在调用者提供的存储空间中,因此localtime_r
可以安全地被多个线程使用,而不会相互干扰。
struct tm 结构体如下所示:
struct tm {
int tm_sec; /* 秒(0-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 年到现在经过的年数) */
int tm_wday; /* 星期(0-6, 星期日 Sunday = 0、星期一=1…) */
int tm_yday; /* 一年里的第几天(0-365, 1 Jan = 0) */
int tm_isdst; /* 夏令时 */
};
3.3 gmtime和gmtime_r函数
gmtime
和 gmtime_r
函数都用于将Linux时间戳转换为UTC的日期和时间,并不是计算机的本地时间,这是 与localtime的唯一区别,函数原型如下所示:
#include <time.h>
struct tm *gmtime(const time_t *timep);
struct tm *gmtime_r(const time_t *timep, struct tm *result);
3.4 mktime()函数
mktime()函数与 localtime()函数相反, mktime()可以将使用 struct tm 结构体表示的分解时间转换为 time_t时间(日历时间) ,同样这也是一个 C 库函数,其函数原型如下所示:
#include <time.h>
time_t mktime(struct tm *tm);
- tm: 需要进行转换的 struct tm 结构体变量对应的指针。
3.5 asctime 与asctime_r函数
asctime()函数与 ctime()函数的作用一样,也可将时间转换为可打印输出的字符串形式,与 ctime()函数的区别在于, ctime()是将 time_t 时间转换为固定格式字符串、而 asctime()则是将 struct tm 表示的分解时间转换为固定格式的字符串。 asctime()函数原型如下所示:
#include <time.h>
char *asctime(const struct tm *tm);
char *asctime_r(const struct tm *tm, char *buf);
- tm: 需要进行转换的 struct tm 表示的时间。
- buf: 可重入版本函数 asctime_r 需要额外提供的参数 buf,指向一个缓冲区,用于存放转换得到的字符串。
3.6 strftime 函数
strftime()函数也可以将一个 struct tm 变量表示的分解时间转换为为格式化字符串,并且在功能上比 asctime()和 ctime()更加强大,它可以根据自己的喜好自定义时间的显示格式,strftime()函数原型如下所示:
#include <time.h>
size_t strftime(char *s, size_t max, const char *format, const struct tm *tm);
- s: 指向一个缓存区的指针,该缓冲区用于存放生成的字符串。
- max: 字符串的最大字节数。
- format: 这是一个用字符串表示的字段, 包含了普通字符和特殊格式说明符,可以是这两种字符的任意组合。 特殊格式说明符将会被替换为 struct tm 结构体对象所指时间的相应值,
以下是
strftime
函数的一些常见格式化指令:
%a: 星期的缩写名。 |
%A :星期的全名。 |
|
|
|
|
|
|
|
|
|
|
%Y :四位数的年份。 |
3.7 综合示例
下面的示例使用 ctime_r
、localtime_r
、gmtime_r
、mktime
、asctime_r
和 strftime
函数演示了如何安全地获取当前时间,转换为本地时间、UTC时间,以及如何格式化时间字符串。
#include <stdio.h>
#include <time.h>
int main()
{
time_t rawtime;
struct tm result_tm;
char buffer[26]; // 足够存储 "Wed Jan 03 09:06:15 1984\n\0" 的长度
// 获取当前时间戳
time(&rawtime);
// 使用 ctime_r 安全地转换时间戳为本地时间字符串
if (ctime_r(&rawtime, buffer) != NULL) {
printf("Current time (ctime_r): %s", buffer);
}
// 使用 localtime_r 安全地转换时间戳为本地tm结构
if (localtime_r(&rawtime, &result_tm) != NULL) {
printf("Local time (localtime_r): %d-%02d-%02d %02d:%02d:%02d\n",
result_tm.tm_year + 1900, result_tm.tm_mon + 1,
result_tm.tm_mday, result_tm.tm_hour,
result_tm.tm_min, result_tm.tm_sec);
}
// 使用 gmtime_r 安全地转换时间戳为UTC tm结构
if (gmtime_r(&rawtime, &result_tm) != NULL) {
printf("UTC time (gmtime_r): %d-%02d-%02d %02d:%02d:%02d\n",
result_tm.tm_year + 1900, result_tm.tm_mon + 1,
result_tm.tm_mday, result_tm.tm_hour,
result_tm.tm_min, result_tm.tm_sec);
}
// 使用 mktime 将本地tm结构转换为时间戳
rawtime = mktime(&result_tm);
if (rawtime == (time_t)-1) {
perror("mktime failed");
} else {
printf("Time stamp (mktime): %ld\n", rawtime);
}
// 使用 asctime_r 安全地转换tm结构为本地时间字符串
if (asctime_r(&result_tm, buffer) != NULL) {
printf("Time (asctime_r): %s", buffer);
}
// 使用 strftime 格式化日期和时间
if (strftime(buffer, sizeof(buffer), "%Y-%m-%d %H:%M:%S", &result_tm) > 0) {
printf("Formatted time (strftime): %s\n", buffer);
}
return 0;
}
运行结果如下:
4 设置时间 settimeofday
settimeofday
是一个在 Linux系统中使用的系统调用,用于设置当前的UTC时间和时钟的微秒部分。函数原型如下:
#include <sys/time.h>
int settimeofday(const struct timeval *tv, const struct timezone *tz);
tv
:指向struct timeval
的指针,包含了新的秒和微秒值。tz
:指向struct timezone
的指针,包含了关于时区的信息,比如夏令时的偏移量。如果不需要设置时区,这个参数可以设置为NULL
。