嵌入式Linux系统编程 — 5.2 Linux系统时间与日期

目录

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获取时间

timegettimeofday 是两个在Linux系统中用于获取时间的函数,分别定义在 <time.h><sys/time.h> 头文件中。

2.1 time函数

time 函数用于获取自1970年1月1日(UTC)以来经过的秒数,函数原型:

#include <time.h>

time_t time(time_t *t);
  • 如果 t 参数不为 NULLtime 函数还会将当前时间戳复制到 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 示例程序

以下是使用 timegettimeofday 函数的示例:

#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函数

gmtimegmtime_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:星期的全名。

%b:月份的缩写名。

%B:月份的全名。

%c:适合人类阅读的日期和时间。

%d:一个月中的第几天(01-31)。

%H:小时(24小时制,00-23)。

%I:小时(12小时制,01-12)。

%m:月份(01-12)。

%M:分钟(00-59)。

%p:AM或PM。

%S:秒(00-59)。

%Y:四位数的年份。

3.7 综合示例

下面的示例使用 ctime_rlocaltime_rgmtime_rmktimeasctime_rstrftime 函数演示了如何安全地获取当前时间,转换为本地时间、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
  • 7
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

几度春风里

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值