40 C 语言日期与时间函数详解:time、ctime、difftime、clock(含 UTC/本地时间转换)
1 UTC 时间与本地时间
1.1 UTC 时间
- 定义:UTC(Coordinated Universal Time,协调世界时)是全球统一的时间标准,以原子钟的高精度计时为基础,独立于地球自转。
- 特点:
- 不受地理位置或时区影响,是全球各时区统一参考的时间基准。
- 通过引入闰秒(每年最多增加或减少 1 秒)进行调整,以补偿地球自转速度的微小变化。
- 确保与基于地球自转的天文时间(UT1)之间的偏差不超过 0.9 秒。
- 示例:2025-05-20 17:20:00 UTC 表示全球统一的时间点。
1.2 本地时间
- 定义:本地时间(Local Time) 是指将协调世界时(UTC)根据所在时区的偏移量进行调整后的时间,用于满足日常生活中对时间显示和使用的需求。
- 时区(Time Zone):
- 全球被划分为 24 个主要时区,相邻时区之间通常相差 1 小时。
- 部分国家或地区采用 30 分钟或 45 分钟 的非整点时区偏移,如印度(UTC+5:30)、尼泊尔(UTC+5:45)。
- 时区通常以 UTC+X 或 UTC−X 的形式表示:
- UTC+8(如北京时间)表示比 UTC 快 8 小时。
- UTC−5(如美国东部标准时间)表示比 UTC 慢 5 小时。
- 夏令时(Daylight Saving Time, DST):
- 某些地区在夏季实行夏令时,通常将时间提前 1 小时(把钟表的时间从当前时间向后调快 1 小时),以更好地利用自然光照,节约能源。
- 夏令时的开始与结束时间因国家和地区而异,可能导致本地时间与 UTC 的偏移在一年中发生临时变化。
- 示例:假设当前 UTC 时间为:2025-05-20 12:00:00
- 北京(UTC+8) 的本地时间为:2025-05-20 20:00:00
- 纽约(UTC−5,非夏令时期间) 的本地时间为:2025-05-20 07:00:00
1.3 UTC 与本地时间的转换
- 转换公式:
- 本地时间 = UTC 时间 + 时区偏移量(需考虑是否启用夏令时)
- UTC 时间 = 本地时间 - 时区偏移量(需考虑是否启用夏令时)
- 示例:
- 若北京时间(UTC+8,非夏令时)为 2025-05-20 10:00:00,则对应的 UTC 时间为:
- UTC 时间 = 10:00 - 8 = 02:00,即 2025-05-20 02:00:00
- 若 UTC 时间为 2025-05-20 12:00:00,则纽约时间(UTC-5,非夏令时)为:
- 本地时间 = 12:00 + (-5) = 07:00,即 2025-05-20 07:00:00
- 若北京时间(UTC+8,非夏令时)为 2025-05-20 10:00:00,则对应的 UTC 时间为:
1.4 时间戳
- 定义:时间戳(Timestamp)通常表示自 1970 年 1 月 1 日 00:00:00 UTC(称为 Unix 纪元)以来经过的秒数。
- 特点:
- 是 UTC 时间的一种数值化表示形式,不依赖于任何时区。
- 便于程序进行时间的比较、存储和计算。
2 time() 函数
2.1 函数原型
#include <time.h> // 必须包含此头文件才能使用 time() 函数time_t time(time_t *timer);
2.2 功能说明
time() 函数用于获取当前的日历时间(即从 1970 年 1 月 1 日 00:00:00 UTC 开始经过的秒数,不包括闰秒),并将其作为 time_t 类型的值返回。
time_t 类型通常是一个长整型(如 long 或 long long),用于表示时间戳(秒数)。在 printf 中输出时,常用 %ld 或 %lld 作为占位符,具体取决于平台。最常用写法(搭配强制转换):printf("%ld", (long)now) 。
- 参数:
- timer:一个指向 time_t 类型变量的指针。
- 如果该参数不为 NULL,则当前时间也会被存储到该指针指向的变量中;
- 若为 NULL,则仅返回时间值。
- timer:一个指向 time_t 类型变量的指针。
- 返回值:
- 成功时返回当前的时间值(以秒为单位的 time_t 类型)。
- 如果失败(例如系统无法获取当前时间),返回 (time_t)(-1)。
2.3 注意事项
- 精度限制:time() 的精度是秒级,不能获取毫秒或微秒级别的时间。
- 跨平台兼容性:time() 是标准 C 库函数,在所有支持 C 标准库的平台上都可用。
- 避免频繁调用:虽然性能通常不是问题,但在高并发或实时系统中应谨慎使用。
- 错误处理:虽然很少发生错误,但建议检查返回值是否为 (time_t)-1。
2.4 应用场景
- 记录日志时间戳:用于生成带有时间信息的日志内容。
- 程序运行计时:可以结合两次调用 time() 来计算程序执行时间(适用于秒级精度)。
- 超时判断:用于判断某个操作是否在规定时间内完成。
- 文件/数据有效期控制:如生成带有时效性的缓存文件或令牌。
- 用户界面显示时间:在 GUI 或 CLI 中显示当前日期和时间。
- 生成唯一标识符:基于时间戳生成简单的唯一 ID(注意重复风险)。
2.5 示例程序
#include <stdio.h>
#include <time.h> // 必须包含此头文件才能使用 time() 函数int main()
{time_t current_time; // 声明一个 time_t 类型的变量来存储当前时间戳// 情况 1:参数为 NULL,仅返回当前时间值time_t returned_time = time(NULL);// 可选的检查返回值if (returned_time == (time_t)-1){printf("无法获取当前时间(参数为 NULL 的情况)。\n");}else{printf("情况1(参数为 NULL):当前时间戳:%ld\n", (long)returned_time);}// 情况 2:参数为非 NULL,当前时间会被存储到指针指向的变量中time(¤t_time); // 传入变量的地址,将当前时间戳存储到该变量中// 一般不检查返回值,直接使用printf("情况2(参数为非 NULL):当前时间戳:%ld\n", (long)current_time);return 0;
}
程序在 VS Code 中的运行结果如下所示:
3 ctime() 函数
3.1 函数原型
#include <time.h> // 必须包含此头文件才能使用 ctime() 函数char *ctime(const time_t *timer);
3.2 功能说明
ctime() 函数用于将 time_t 类型表示的时间值(即时间戳)转换为一个可读性良好的字符串形式,返回的是一个表示本地时间的字符串。
- 参数:
- timer:指向一个 time_t 类型变量的指针,表示要转换的时间值。
- 返回值:
- 成功时返回一个指向静态字符数组的指针,该数组中保存了格式化后的时间字符串;
- 如果失败(如传入的 timer 为 NULL 或系统内部错误),则返回 NULL。
3.3 注意事项
- 自动换行符:返回的字符串末尾包含一个 \n 换行符。
- 结果不可修改:返回的字符串是只读的,不应尝试修改其内容。
- 依赖系统本地时区:ctime() 返回的是本地时间,受操作系统时区设置影响。
- 返回值需检查:虽然很少失败,但应检查是否为 NULL。
3.4 应用场景
- 打印当前时间:快速输出程序运行时的当前时间。
- 日志记录:在日志信息前加上可读的时间戳。
- 调试辅助:在调试过程中查看当前时间。
- 简单时间显示:适用于不需要自定义格式的界面或控制台输出。
3.5 示例程序
#include <stdio.h>
#include <time.h> // 必须包含此头文件才能使用 time() 和 ctime() 函数int main()
{time_t current_time; // 声明一个 time_t 类型的变量来存储当前时间戳// 情况 1:参数为 NULL,仅返回当前时间值time_t returned_time = time(NULL);// 可选的检查返回值if (returned_time == (time_t)-1){printf("无法获取当前时间(参数为 NULL 的情况)。\n");}else{printf("情况1(参数为 NULL):当前时间戳:%ld\n", (long)returned_time);// 使用 ctime 输出可读时间字符串printf("情况1 对应的本地时间:%s", ctime(&returned_time)); // 不需要换行符,ctime() 函数会自动添加}// 情况 2:参数为非 NULL,当前时间会被存储到指针指向的变量中time(¤t_time); // 传入变量的地址,将当前时间戳存储到该变量中printf("情况2(参数为非 NULL):当前时间戳:%ld\n", (long)current_time);// 使用 ctime 输出可读时间字符串printf("情况2 对应的本地时间:%s", ctime(¤t_time)); // 不需要换行符,ctime() 函数会自动添加return 0;
}
程序在 VS Code 中的运行结果如下所示:
4 difftime() 函数
4.1 函数原型
#include <time.h> // 必须包含此头文件才能使用 difftime() 函数double difftime(time_t time2, time_t time1);
4.2 功能说明
difftime() 函数用于计算两个时间点之间的时间差(以秒为单位)。这两个时间点通常是由 time() 函数获取的 time_t 类型的时间戳。
- 参数:
- time2:结束时间,一个 time_t 类型值。
- time1:开始时间,一个 time_t 类型值。
- 返回值:
- 返回 time2 - time1 的差值,结果以 double 类型表示,单位为秒;
- 如果 time2 比 time1 小(即结束时间早于开始时间),则返回负数。
4.3 注意事项
- 返回值为 double:可以表示非整数秒的时间差(如某些系统支持更高精度时间),适合更精确的计时。
- 顺序影响结果:difftime(time2, time1) 表示从 time1 到 time2 的时间差,注意参数顺序。
- 常用于性能测试:适用于粗略测量代码执行时间(如秒级或亚秒级)。
- 可以跨时区比较:时间戳本质上是 UTC 时间,不受本地时区影响,因此适合用于跨时区的时间差计算。
4.4 应用场景
- 程序运行时间统计:记录某段代码执行前后的时间戳,并计算耗时。
- 超时控制:判断某个操作是否在规定时间内完成。
- 用户行为分析:记录用户操作之间的间隔时间。
- 日志记录:输出事件发生的时间间隔,便于后续分析。
- 定时任务调度:用于判断是否满足触发条件。
4.5 示例程序
#include <stdio.h>
#include <time.h> // 必须包含此头文件才能使用 time() 和 difftime() 函数int main()
{time_t start_time, end_time; // 定义两个 time_t 变量用于保存时间戳printf("程序开始计时...\n");start_time = time(NULL); // 获取开始时间// 模拟耗时操作(通过循环延时)for (unsigned long long i = 0; i < 10000000000LL; ++i); // 简单延迟end_time = time(NULL); // 获取结束时间// 使用 difftime 计算时间差double elapsed_seconds = difftime(end_time, start_time);printf("程序结束计时。\n");printf("经过的时间:%lf 秒\n", elapsed_seconds);return 0;
}
程序在 VS Code 中的运行结果如下所示:
5 clock() 函数
5.1 函数原型
#include <time.h> // 必须包含此头文件才能使用 clock() 函数clock_t clock(void);
5.2 功能说明
clock() 函数返回自程序启动以来(或程序进入当前状态以来)的处理器时间(CPU 时间),单位通常是 “时钟滴答”(clock ticks)。该时间以 clock_t 类型表示,通常用于测量一段代码的 CPU 执行时间。
- 返回值:
- 返回当前进程使用的处理器时间(CPU 时间),单位为 CLOCKS_PER_SEC(每秒的时钟滴答数),而不是秒数;
- 示例:如果 CLOCKS_PER_SEC 是 1000000,则 clock() 返回的值除以 1000000 可以得到秒数。
- 因此,clock() 返回的滴答数除以 CLOCKS_PER_SEC 可以得到以秒为单位的 CPU 时间。
- 返回 (clock_t)(-1)(通常是 -1),并可能设置 errno(但标准未明确要求设置 errno,具体行为依赖实现)。
- 返回当前进程使用的处理器时间(CPU 时间),单位为 CLOCKS_PER_SEC(每秒的时钟滴答数),而不是秒数;
5.3 注意事项
- 仅限于当前进程:只能用于测量当前进程内的 CPU 使用情况。
- 精度问题:在某些系统上,CLOCKS_PER_SEC 的值可能较低(如 100),导致 clock() 的分辨率不高,适合粗略估计而非高精度计时。
- 平台差异性:不同操作系统实现可能有所不同,尤其注意 CLOCKS_PER_SEC 的值。
- 返回类型:返回类型是 clock_t,需根据具体平台将其转换为秒(除以 CLOCKS_PER_SEC)或其他时间单位进行解释。
5.4 应用场景
- 性能分析:评估特定算法或代码段的执行效率。
- 调试辅助:帮助开发者了解某部分代码是否耗时过长。
- 资源监控:监测程序运行过程中对 CPU 资源的占用情况。
- 测试工具开发:构建自动化测试框架中的一部分,用于度量测试案例的执行速度。
5.5 示例程序
#include <stdio.h>
#include <time.h> // 必须包含此头文件才能使用 clock() 函数int main()
{// 记录开始时间clock_t start_time = clock();// 模拟一个耗时的操作for (unsigned long long i = 0; i < 10000000000LL; ++i); // 简单延迟// 记录结束时间clock_t end_time = clock();// 计算消耗的 CPU 时间,并将其转换为秒(除以 CLOCKS_PER_SEC)double cpu_time_used = ((double)(end_time - start_time)) / CLOCKS_PER_SEC;printf("模拟操作消耗的 CPU 时间: %lf 秒\n", cpu_time_used);return 0;
}
程序在 VS Code 中的运行结果如下所示:
6 日期与时间函数总结
函数名 | 功能 | 参数 | 返回值 |
---|---|---|---|
time | 获取当前时间戳(自 1970-01-01 00:00:00 UTC 起经过的秒数) | time_t *timer:可为 NULL,用于接收时间戳 | 成功返回时间戳(time_t 类型),失败返回 (time_t)-1 |
ctime | 将时间戳转换为可读的本地时间字符串(格式固定,带换行符) | const time_t *timer:指向时间戳的指针 | 成功返回指向静态字符串的指针,失败返回 NULL |
difftime | 计算两个时间点之间的时间差(以秒为单位) | time_t time2, time_t time1:两个时间点 | 返回 time2 - time1 的差值(double 类型,单位为秒) |
clock | 获取当前进程使用的 CPU 时间(从程序启动开始计算) | 无参数 | 成功返回 CPU 使用时间(以时钟周期为单位),失败返回 (clock_t)-1 |
补充说明:
- time_t 类型:通常为长整型(如 long 或 long long),用于表示自 Unix 纪元(1970-01-01 00:00:00 UTC)以来经过的秒数,即时间戳。
- ctime() 的线程安全性:该函数返回一个指向内部静态缓冲区的指针,内容在每次调用时会被覆盖。
- difftime() 的优势:该函数基于标准 C 定义,能够准确计算两个时间点之间的差值,单位为秒(支持小数),适用于跨平台、跨时区的时间差比较,是推荐的标准方法。
- clock() 的局限性:它返回的是程序所占用的 CPU 时间,而非真实世界时间(wall-clock time),因此不适合用于测量 I/O 操作、睡眠或等待外部事件的时间。