程序源码:https://pan.baidu.com/s/1j5_EM_ah-E__wGXKmJbasg?pwd=kmrf
1.时间格式转换
1.1 localtime()
函数原型:
#include <time.h>
struct tm *localtime(const time_t *timer); // 非线程安全
// 线程安全版本
struct tm *localtime_r(const time_t *timer, struct tm *result);
功能:将 time_t 类型的时间戳(自 1970-1-1 的秒数)转换为本地时间的 struct tm 结构体。
输入参数:
timer:指向 time_t 变量的指针。
result(仅 localtime_r):用户提供的 struct tm 存储地址。
返回值:
成功返回指向 struct tm 的指针,失败返回 NULL
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](0 代表 1 月)
int tm_year; // 年(自 1900 年的偏移,如 2024 年为 124)
int tm_wday; // 星期 [0-6](0 代表周日)
int tm_yday; // 年中的第几天 [0-365]
int tm_isdst; // 夏令时标志(>0 启用,=0 禁用,<0 未知)
};
1.1.1 非线程安全版本(localtime)
程序:
#include <stdio.h>
#include <time.h>
#if 0
// struct tm *localtime(const time_t *timep);
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]
int tm_yday; // 年中的第几天 [0-365]
int tm_isdst; // 夏令时标志
};
#endif
int main()
{
time_t now = time(NULL); // 获取当前时间戳
struct tm *tm_info = localtime(&now); // 转换为本地时间
if (tm_info == NULL)
{
perror("localtime failed");
return -1;
}
printf("当前时间:%04d-%02d-%02d %02d:%02d:%02d\n",
tm_info->tm_year + 1900, tm_info->tm_mon + 1, tm_info->tm_mday,
tm_info->tm_hour, tm_info->tm_min, tm_info->tm_sec);
// 输出如 2025-02-06 14:30:29
return 0;
}
运行结果:
1.1.2 线程安全版本(localtime_r)
程序:
#include <stdio.h>
#include <time.h>
#if 0
// struct tm *localtime(const time_t *timep);
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]
int tm_yday; // 年中的第几天 [0-365]
int tm_isdst; // 夏令时标志
};
#endif
int main()
{
time_t now = time(NULL); // 获取当前时间戳
struct tm tm_info; // 用户分配的存储空间
// 显式传递存储地址
if (localtime_r(&now, &tm_info) == NULL)
{
perror("localtime_r failed");
return -1;
}
printf("当前时间:%04d-%02d-%02d %02d:%02d:%02d\n",
tm_info.tm_year + 1900, tm_info.tm_mon + 1, tm_info.tm_mday,
tm_info.tm_hour, tm_info.tm_min, tm_info.tm_sec);
// 输出如 2025-02-06 14:30:29
return 0;
}
运行结果:
1.2 gmtime()
将 time_t 类型的时间戳(自 Epoch 的秒数)转换为 UTC 时间(世界标准时间)。
函数原型:
#include <time.h>
struct tm *gmtime(const time_t *timer); // 非线程安全
// 线程安全版本
struct tm *gmtime_r(const time_t *timer, struct tm *result);
功能:将 time_t 类型的时间戳(自 Epoch 的秒数)转换为 UTC 时间(协调世界时)。
输入参数:
timer:指向 time_t 变量的指针。
result(仅 gmtime_r):用户提供的 struct tm 存储地址。
返回值:
成功返回指向 struct tm 的指针,失败返回 NULL。
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](0 代表 1 月)
int tm_year; // 年(自 1900 年的偏移,如 2024 年为 124)
int tm_wday; // 星期 [0-6](0 代表周日)
int tm_yday; // 年中的第几天 [0-365]
int tm_isdst; // 夏令时标志(UTC 时间中通常为 0)
};
1.2.1非线程安全版本(gmtime)
程序:
#include <stdio.h>
#include <time.h>
/*
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](0 代表 1 月)
int tm_year; // 年(自 1900 年的偏移,如 2024 年为 124)
int tm_wday; // 星期 [0-6](0 代表周日)
int tm_yday; // 年中的第几天 [0-365]
int tm_isdst; // 夏令时标志(UTC 时间中通常为 0)
};
*/
int main()
{
time_t now = time(NULL); // 获取当前时间戳
struct tm *tm_utc = gmtime(&now); // 转换为 UTC 时间
if (tm_utc == NULL)
{
perror("gmtime failed");
return -1;
}
printf("UTC 时间:%04d-%02d-%02d %02d:%02d:%02d\n",
tm_utc->tm_year + 1900, tm_utc->tm_mon + 1, tm_utc->tm_mday,
tm_utc->tm_hour, tm_utc->tm_min, tm_utc->tm_sec);
// 输出如 UTC 时间:2024-06-06 06:30:00(假设本地时区为 UTC+8)
return 0;
}
运行结果:
1.2.2线程安全版本(gmtime_r)
程序:
#include <stdio.h>
#include <time.h>
/*
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](0 代表 1 月)
int tm_year; // 年(自 1900 年的偏移,如 2024 年为 124)
int tm_wday; // 星期 [0-6](0 代表周日)
int tm_yday; // 年中的第几天 [0-365]
int tm_isdst; // 夏令时标志(UTC 时间中通常为 0)
};
*/
int main()
{
time_t now = time(NULL); // 获取当前时间戳
struct tm tm_utc; // 用户分配的存储空间
if (gmtime_r(&now, &tm_utc) == NULL)
{
perror("gmtime_r failed");
return -1;
}
printf("UTC 时间:%04d-%02d-%02d %02d:%02d:%02d\n",
tm_utc.tm_year + 1900, tm_utc.tm_mon + 1, tm_utc.tm_mday,
tm_utc.tm_hour, tm_utc.tm_min, tm_utc.tm_sec);
// 输出如 UTC 时间:2024-06-06 06:30:00(假设本地时区为 UTC+8)
return 0;
}
运行结果:
1.2.3 UTC与本地时间偏差
程序:
#include <stdio.h>
#include <time.h>
/*
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](0 代表 1 月)
int tm_year; // 年(自 1900 年的偏移,如 2024 年为 124)
int tm_wday; // 星期 [0-6](0 代表周日)
int tm_yday; // 年中的第几天 [0-365]
int tm_isdst; // 夏令时标志(UTC 时间中通常为 0)
};
*/
int main()
{
time_t now = time(NULL);
struct tm tm_local, tm_utc;
localtime_r(&now, &tm_local); // 本地时间
gmtime_r(&now, &tm_utc); // UTC 时间
// 计算时区偏移(单位:秒)
int time_offset = (tm_local.tm_hour - tm_utc.tm_hour) * 3600 +
(tm_local.tm_min - tm_utc.tm_min) * 60;
printf("UTC 时间 与 本地时区偏移:%d 秒\n", time_offset);
return 0;
}
运行结果:
1.2.4 localtime
与gmtime
对比
函数 | 时区 | 线程安全 | 输入类型 | 输出存储 |
---|---|---|---|---|
localtime() | 本地时间 | 否 | time_t * | 静态内存 |
gmtime() | UTC | 否 | time_t * | 静态内存 |
localtime_r() | 本地时间 | 是 | time_t * | 用户提供的内存 |
gmtime_r() | UTC | 是 | time_t * | 用户提供的内存 |
1.3 mktime()
将本地时间的 struct tm 结构体转换为 time_t 类型的时间戳(自 1970-1-1 以来的秒数),并自动修正字段的非法值。
(1)函数原型:
#include <time.h>
time_t mktime(struct tm *tm);
功能:将本地时间(struct tm)转换为 time_t 类型的时间戳(自1970-1-1的秒数)。
输入参数:指向 struct tm 的指针(时间值可能被修改)。
返回值:
成功返回对应的时间戳。
失败返回 (time_t)-1(如时间超出 time_t 表示范围)
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](0 表示 1 月)
int tm_year; // 年(实际年份 = tm_year + 1900)
int tm_isdst; // 夏令时标志(>0: 生效,0: 不生效,<0: 自动判断)
};
(2)核心特性
①自动修正非法时间值 若输入的 tm 结构体字段(如 tm_mday=35)不合法,mktime() 会自动调整字段至有效值。 示例:
示例一:
struct tm tm = {0};
tm.tm_year = 124; // 2024 年
tm.tm_mon = 1; // 2 月
tm.tm_mday = 35; // 非法日期(2 月最多 29 天)
mktime(&tm); // 修正为 2024-03-06
示例二:
struct tm tm = {0};
tm.tm_year = 124; // 2024 年
tm.tm_mon = 11; // 12 月(合法)
tm.tm_mday = 32; // 非法日期(12 月 32 日)
tm.tm_isdst = -1;
time_t t = mktime(&tm);
// 函数自动修正为 2025 年 1 月 1 日,并更新 tm 结构体字段
printf("修正后日期: %04d-%02d-%02d\n",
tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday);
// 输出:2025-01-01
②依赖时区与夏令时
- 使用 本地时区(由系统或 TZ 环境变量决定)。
- 自动处理夏令时(tm_isdst 字段可强制指定或设为 -1 由系统推断)
1.3.1 将 设置时间 转换为时间戳秒数
程序:
#include <stdio.h>
#include <time.h>
int main()
{
struct tm tmp = {0};
tmp.tm_year = 124; // 2024 年(2024 - 1900)
tmp.tm_mon = 5; // 6 月(0 代表 1 月)
tmp.tm_mday = 15; // 15 日
tmp.tm_hour = 14; // 14 时
tmp.tm_min = 30; // 30 分
tmp.tm_sec = 0; // 0 秒
tmp.tm_isdst = -1; // 由系统推断是否夏令时
//将 tm 转换为 秒数
time_t timestamp = mktime(&tmp);
if (timestamp == -1)
{
perror("mktime failed");
return -1;
}
printf("时间戳秒数:%ld s\n", timestamp);
return 0;
}
运行结果:
1.3.2 自动修正非法时间值
程序:
#include <stdio.h>
#include <time.h>
/*
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](0 表示 1 月)
int tm_year; // 年(实际年份 = tm_year + 1900)
int tm_isdst; // 夏令时标志(>0: 生效,0: 不生效,<0: 自动判断)
};
*/
void test1()
{
struct tm tmp = {0};
tmp.tm_year = 124; // 2024 年
tmp.tm_mon = 11; // 12 月(合法)
tmp.tm_mday = 32; // 非法日期(12 月 32 日)
tmp.tm_isdst = -1;
time_t t = mktime(&tmp);
// 函数自动修正为 2025 年 1 月 1 日,并更新 tm 结构体字段
printf("修正后日期: %04d-%02d-%02d\n",
tmp.tm_year + 1900, tmp.tm_mon + 1, tmp.tm_mday);
// 输出:2025-01-01
}
void test2()
{
struct tm tmp = {0};
tmp.tm_year = 124; // 2024 年
tmp.tm_mon = 11; // 12 月(合法)
tmp.tm_mday = 28; // 28 日(合法)
tmp.tm_hour = 26; // 26时 (不合法,时 [0, 23])
tmp.tm_isdst = -1;
time_t t = mktime(&tmp);
// 函数自动修正为 2025 年 1 月 1 日,并更新 tm 结构体字段
printf("修正后日期: %04d-%02d-%02d %02d时\n",
tmp.tm_year + 1900, tmp.tm_mon + 1, tmp.tm_mday, tmp.tm_hour + 1 );
// 输出: 2024-12-29 03时
}
int main()
{
test1();
printf("==\n");
test2();
return 0;
}
运行结果:
1.3.3 计算两个日期的天数差
程序:
#include <stdio.h>
#include <time.h>
/*
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](0 表示 1 月)
int tm_year; // 年(实际年份 = tm_year + 1900)
int tm_isdst; // 夏令时标志(>0: 生效,0: 不生效,<0: 自动判断)
};
*/
int main()
{
// 2024-06-15 20时
struct tm date1 = { .tm_year=124, .tm_mon=5, .tm_mday=15, .tm_hour=20, .tm_isdst=-1 };
// 2024-06-20 16时
struct tm date2 = { .tm_year=124, .tm_mon=5, .tm_mday=20, .tm_hour=16, .tm_isdst=-1 };
time_t t1 = mktime(&date1);
time_t t2 = mktime(&date2);
//计算两个 time_t 类型时间戳之间的差值(以秒为单位),t2为较晚时间
double diff_days = difftime(t2, t1) / (24 * 3600);
/*
double minutes = duration / 60; // 分钟
double hours = duration / 3600; // 小时
double days = duration / 86400; // 天
*/
printf("相差天数:%.2f\n", diff_days); // 输出 4.83
return 0;
}
运行结果:
1.3.4获取某天是星期几
程序:
#include <stdio.h>
#include <time.h>
/*
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](0 表示 1 月)
int tm_year; // 年(实际年份 = tm_year + 1900)
int tm_isdst; // 夏令时标志(>0: 生效,0: 不生效,<0: 自动判断)
};
*/
int main()
{
struct tm tmp = {.tm_year=124, .tm_mon=8, .tm_mday=26, .tm_isdst=-1};
mktime(&tmp); // 自动填充 tm_wday(星期几)
const char *weekdays[] = {"日", "一", "二", "三", "四", "五", "六"};
printf(" %04d-%02d-%02d 是星期%s\n",tmp.tm_year + 1900, tmp.tm_mon+1, tmp.tm_mday ,weekdays[tmp.tm_wday]);
return 0;
}
运行结果: