在正式介绍这些时间、日期相关的系统调用或 C 库函数之前,需要向大家介绍一些时间相关的基本概念,譬如 GMT 时间、 UTC 时间以及时区等。
GMT时间:GMT是格林尼治平均时间,以地球的本初子午线——通过英国伦敦郊区的格林尼治天文台的经线——为基准。GMT是历史上用来确定时间的标准,它不随地球自转速度的变化而调整。
UTC时间:目前国际上用来替代GMT的官方时间标准。UTC使用高精度的原子钟来测量时间,并且可以添加闰秒以保持与地球自转周期的一致性。UTC的主要目的是为了提供一个稳定的时间标准,不受地球自转速度微小变化的影响。
地区时间:实际,世界上不少国家和地区都不严格按时区来计算时间。为了在全国范围内采用统一的时间,一般都把某一个时区的时间作为全国统一采用的时间。例如,我国把首都北京所在的东 8 区的时间作为全国统一的时间,称为北京时间, 北京时间就作为我国使用的本地时间, 譬如我们电脑上显示的时间就是北京时间。
在 Ubuntu 系统下,可以使用 date 命令查看系统当前的本地时间,可以看到显示出来的字符串后面有一个"CST"字样, CST 在这里其实指的是 China Standard Time(中国标准时间)的缩写,如下所示:
要显示UTC时间,可以使用date -u
或者date --utc
"系统时钟"和"实时时钟"
在计算机系统中,"系统时钟"(System Clock)和"实时时钟"(Real Time Clock, RTC)是两种不同类型的时钟,它们有不同的用途和特性:
系统时钟(System Clock):
实时时钟(Real Time Clock, RTC):
jiffies 的引入
Jiffies是Linux内核中用于时间度量的一个概念,它是一个自系统启动以来的计时器,以固定频率递增。每个jiffy的长度取决于系统的时钟频率,通常在不同系统上会有所不同,但大约相当于几毫秒。Jiffies被用来测量系统的运行时间、调度任务和执行时间相关的计算。由于jiffies与实际时间的秒数不是固定比例,它主要用于内核内部的时间管理,而不是用来获取精确的日期和时间。
time
和 gettimeofday
是两个在Linux系统中用于获取时间的函数,分别定义在 <time.h>
和 <sys/time.h>
头文件中。
time
函数用于获取自1970年1月1日(UTC)以来经过的秒数,函数原型:
#include <time.h>
time_t time(time_t *t);
t
参数不为 NULL
,time
函数还会将当前时间戳复制到 t
指向的位置。-1
并设置 errno
。time()获取到的时间只能精确到秒,如果想要获取更加精确的时间可以使用系统调用 gettimeofday 来实现, gettimeofday()函数提供微秒级时间精度,函数原型如下所示:
#include <sys/time.h>
int gettimeofday(struct timeval *tv, struct timezone *tz);
tv
指向一个 timeval
结构,该结构将被填充当前时间的秒和微秒。tz
指向一个 timezone
结构,如果提供,它将被填充关于本地时区的信息。以下是使用 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;
}
运行结果如下:
通过 time()或 gettimeofday()函数可以获取到当前时间点相对于 1970-01-01 00:00:00 +0000(UTC)这个时间点所经过时间(日历时间) ,所以获取得到的是一个时间段的长度,但是这并不利于我们查看当前时间,下面介绍一些系统调用或 C 库函数,通过这些 API 可以将 time()或gettimeofday()函数获取到的秒数转换为利于查看和理解的形式。
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()。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);
struct tm
的指针,该结构包含了转换后的本地时间。意味着每次调用 localtime
都会覆盖上一次调用的结果,因此它不是线程安全的。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; /* 夏令时 */
};
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);
mktime()函数与 localtime()函数相反, mktime()可以将使用 struct tm 结构体表示的分解时间转换为 time_t时间(日历时间) ,同样这也是一个 C 库函数,其函数原型如下所示:
#include <time.h>
time_t mktime(struct tm *tm);
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);
strftime()函数也可以将一个 struct tm 变量表示的分解时间转换为为格式化字符串,并且在功能上比 asctime()和 ctime()更加强大,它可以根据自己的喜好自定义时间的显示格式,strftime()函数原型如下所示:
#include <time.h>
size_t strftime(char *s, size_t max, const char *format, const struct tm *tm);
以下是 strftime
函数的一些常见格式化指令:
%a: 星期的缩写名。 |
%A :星期的全名。 |
|
|
|
|
|
|
|
|
|
|
%Y :四位数的年份。 |
下面的示例使用 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;
}
运行结果如下:
settimeofday
是一个在 Linux系统中使用的系统调用,用于设置当前的UTC时间和时钟的微秒部分。函数原型如下:
#include <sys/time.h>
int settimeofday(const struct timeval *tv, const struct timezone *tz);
tv
:指向 struct timeval
的指针,包含了新的秒和微秒值。tz
:指向 struct timezone
的指针,包含了关于时区的信息,比如夏令时的偏移量。如果不需要设置时区,这个参数可以设置为 NULL
。因篇幅问题不能全部显示,请点此查看更多更全内容
Copyright © 2019- igbc.cn 版权所有 湘ICP备2023023988号-5
违法及侵权请联系:TEL:199 1889 7713 E-MAIL:2724546146@qq.com
本站由北京市万商天勤律师事务所王兴未律师提供法律服务