C语言 <time.h> 时间日期处理

C语言的头文件 <time.h> 中,提供了大量关于时间、日期的操作。在许多程序中我们经常回合时间日期打交道,例如日志系统会记录每一个操作的时间,优化程序时也会计算某一函数的执行速度。这些都需要时间、日期等信息处理。

三种类型

在<time.h> 中定义了三种时间、日期的数据类型

  1. clock_t :用于记录机器时间
  2. time_t : 紧凑的日期、时间编码(日历时间)
  3. struct tm : 将时间分解成 秒、分、时等,分别存储在各个成员中(分解时间)

struct tm 结构体成员(所有成员均为 int ):

  • tm_sec 秒
  • tm_min 分钟
  • tm_hour 小时
  • tm_mday 月内第几天
  • tm_mon 一月以来的月数
  • tm_year 1900 年以来的年数
  • tm_wday 星期日以来的天数
  • tm_yday 1月1日以来的天数
  • tm_isdst 夏令时标志(有效为正、无效为0、未知为负)

以上三种数据类型分别有不同的用途。clock_t 和 time_t 在笔者的机器上分别为 long 、__time64_t,都是整数类型,但是C语言并没有强制要求具体类型,只要求了这两种类型必须能够计算。因此我们可以方便的转换其数据

clock_t 类型主要用来计数时钟周期,因此非常适合用来记录时间差,特别是在调试程序时用于记录函数的处理时间,因为时间差是时钟周期级别,因此如果想要转化为秒还需要将差值除以一个宏常量 CLOCKS_PER_SEC

#include <time.h>
#include <stdio.h>
void fun()
{
clock_t start_clock = clock();
    
    // codes
    
printf("time:%d\n", clock() - start_clock());
    return;
}

time_t 类型存储了从 1970年至今的秒数,和 clock_t 的计量单位是不一样的,clock_t 单位是始终周期,而 time_t 单位是秒。因此该类型非常适合用来转换我们正常使用的时间、日期格式。即 某年某月某天某时某分某秒 。

struct tm 类型是一个叫结构体,每一个成员都分别存储了对应的时间日期信息,因此便于我们进行个性化的显示,可以组合出不同的显示格式,并记录在字符串中,最后通过字符串进行打印输出。但是需要注意每一个成员的值可能不是基于0开始

例如:tm_mon 是从1月开始计算的,1月值为0,2月值为1,3月值为2 以此类推。tm_year 计算从 1970 年至今年的差值,因此 2023 年的值,对应 2023 - 1970 = 53.

时间处理函数

因为我们正常的阅读习惯都是年月日、时分秒,而以上三种结构都没有很直接的显示这种日期格式。因此需要进行时间格式上的处理和转换,<time.h>头文件中已经为我们提供了一些处理转换函数

clock_t clock(); //返回从程序开始执行到执行该函数时的处理器时间,若想转换秒,需要除以 CLOCKS_PER_SEC 常量,在main函数开始处调用该函数可以得出程序启动的初始化阶段的时间消耗
double difftime(time_t time1, time_t time0); // 返回从 time0 到 time1 之间的秒数,time0 是较早的时间
time_t mktime(struct * ptm); // 计算指针 ptm 指向的 struct tm 结构时间,并规范化(例如天数大于当月的天数限制,则会发生进制,其余同理),之后返回 日历时间(从1970至ptm指向结构的时间结果)
time_t time(time_t *timer); // 返回日历时间 从 1970 至今的秒数,若 timer 不为 NULL,也会给予同样的值

 其中 mktime() 需要注意,它可以转换不正确的日期为正确日期。例如 若 tm_mon 成员为 15,这显然不正确,此时调用 mktime 后,将会对传递进来的参数进行检查,将tm_mon 成员改成 15 - 12 = 3,同时 tm_year + 1。这类似于发生了“进位“。这一过程对于时、分、秒,同样有效。

时间转换函数

三种时间类型各有千秋,因此需要在合适的场景使用合适的格式,但这往往会伴随着转换。头文件中也向我们提供了相应的转换函数,供我们在不同类型间转换,同时还可以写入字符串,进而显示时间日期

转换函数主要提供了 日历时间(time_t)、分解时间(struct tm) 、字符串 三者间的转换功能。

char * asctime(const struct tm * timeptr); // 传进 struct tm 结构体,返回固定格式的 时间日期字符串
char * ctime(const time_t * timerptr); // 传进 time_t 类型指针,返回 本地格式的时间日期字符串,这与不同地区的格式有关
// 以上两种返回的字符串指针,均为静态变量,因此后续无需对返回的指针变量进行释放操作,每一次调用都会更新该静态变量的值

struct tm * gmtime(const time_t &timerptr); // 传进 time_t 类型指针,返回指向分解时间结构体的指针
struct tm * localtime(const time_t &timerptr); //传进 time_t 类型指针,返回指向分解时间结构体的指针
// 以上两种函数返回的结构体是静态的,因此后续无需释放该指针指向的空间,二者区别在于返回的格式不同,localtime 返回本地格式,gmtime 返回协调时间格式

size_t strftime(char * restrict s, size_t max_size, const char * restrict format, const struct tm restrict timeptr);
// 将传递进来的 struct tm 结构体转换成正确的日期格式,并写入 字符串 s 中,不超过 max_size 规定的字符数
// 该函数功能与 asctime 类似,但额外提供了大量对于时间进行格式化的控制。


// ctime(&time_ptr) 等价于 asctime(localtime(&timeptr));

 上面的函数中 strftime() 函数是最复杂但也是最强大的,很多时候只需要这个函数就可以得到很好的效果了,其中第三个格式化参数类似于 printf 中的格式化字符串,两者在使用上几乎没有差别,但是格式化控制符有很多,不过大部分都用不到,这里列出几个常用的以供参考

/* strftime 函数格式化符号参考及示例
*    %c 完整的日期和时间 (Wed Sep  6 16:43:26 2023)
*    %D mm/dd/yy (09/06/23) 仅显示 月/日/年
*    %F yyyy-mm-dd (2023-09-06) 仅显示 年-月-日
*    %T hh:mm:ss (16:43:26) 仅显示 时:分:秒
*/ 

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

int main(int argc, const char * args[])
{
    char date_time[100];
    // %c 格式示例
    strftime(date_time, sizeof(date_time), "Date-Time Format:%c\n", pt);
    printf(date_time); //Date-Time Format:Wed Sep  6 16:43:26 2023

    // %D 格式示例
    strftime(date_time, sizeof(date_time), "Date-Time Format:%D\n", pt);
    printf(date_time); // Date-Time Format:09/06/23

    // %F 格式示例
    strftime(date_time, sizeof(date_time), "Date-Time Format:%F\n", pt);
    printf(date_time); // Date-Time Format:2023-09-06

    // %F %T 格式示例
    strftime(date_time, sizeof(date_time), "Date-Time Format:%F %T\n", pt);
    printf(date_time); // Date-Time Format:2023-09-06 16:43:26
    
    return 0;
}

 

posted @ 2023-09-06 16:53  夏蝉冬雪|春华秋实  阅读(834)  评论(0)    收藏  举报