C++中获取当年年初UTC纪元时间的可移植方案求助
可移植解决方案:分场景适配的两种方案
你遇到的这个问题我在嵌入式开发中也碰到过——timegm()确实是个移植性大坑,很多交叉编译器为了精简都砍掉了它,而mktime()又绑定了本地时区,直接替换肯定会出问题。下面给你两种实用的方案,分别适配C++11及以上和纯C的场景:
方案一:C++11及以上用std::chrono(推荐)
std::chrono是C标准库提供的时间处理工具,跨平台性拉满,还能避免C风格API的线程安全问题。根据你使用的C版本,又分两种实现:
C++20及以上:最简洁的实现
C++20新增了year_month_day等日历类型,能直接构造UTC时间点,完全不需要手动处理时区和闰年:
#include <chrono> #include <ctime> time_t getEpochTimeAtSOY() { // 获取当前UTC时间点 const auto now_utc = std::chrono::system_clock::now(); const std::time_t now_time = std::chrono::system_clock::to_time_t(now_utc); // 转换为UTC日历结构,提取当前年份 std::tm utc_tm{}; if (gmtime_r(&now_time, &utc_tm) == nullptr) { // 优先用线程安全的gmtime_r return -1; // 错误处理,可根据项目需求调整 } // 构造当年1月1日00:00:00的UTC时间点 const std::chrono::year_month_day ymd{ std::chrono::year(utc_tm.tm_year + 1900), std::chrono::January, std::chrono::day(1) }; const auto year_start = std::chrono::sys_days(ymd); // 转换为time_t类型返回 return std::chrono::system_clock::to_time_t(year_start); }
C11/C17兼容版:临时切换时区 trick
如果你的编译器不支持C++20,也可以用临时修改时区的方法让mktime()返回UTC时间:
#include <chrono> #include <ctime> #include <cstdlib> time_t getEpochTimeAtSOY() { // 获取当前UTC年份 const auto now_utc = std::chrono::system_clock::now(); const std::time_t now_time = std::chrono::system_clock::to_time_t(now_utc); std::tm utc_tm{}; if (gmtime_r(&now_time, &utc_tm) == nullptr) { return -1; } // 构造当年年初的UTC时间结构 std::tm year_start_tm{}; year_start_tm.tm_year = utc_tm.tm_year; year_start_tm.tm_mon = 0; // 0代表1月 year_start_tm.tm_mday = 1; // 1号 // 临时将时区切换为UTC,让mktime按UTC解析 char* old_tz = getenv("TZ"); setenv("TZ", "", 1); tzset(); const time_t year_start_time = mktime(&year_start_tm); // 恢复原来的时区设置 if (old_tz != nullptr) { setenv("TZ", old_tz, 1); } else { unsetenv("TZ"); } tzset(); return year_start_time; }
注意:这个方法在多线程环境下可能有问题,如果是多线程场景,建议用下面的纯C方案。
方案二:纯C实现自定义timegm(极致移植)
如果你的项目是纯C或者不想依赖C++标准库,可以自己实现一个简化版的timegm,完全不依赖平台特定函数:
#include <time.h> #include <stdlib.h> // 判断是否为闰年(传入的是tm_year格式:实际年份-1900) static int is_leap(int tm_year) { const int year = tm_year + 1900; if (year % 4 != 0) return 0; else if (year % 100 != 0) return 1; else if (year % 400 != 0) return 0; else return 1; } // 自定义timegm:将UTC的tm结构转换为time_t static time_t my_timegm(const struct tm *tm) { time_t total_seconds = 0; const int target_year = tm->tm_year; // 计算1970年到目标年份前一年的总天数 for (int y = 70; y < target_year; y++) { total_seconds += (is_leap(y) ? 366 : 365) * 24LL * 3600LL; } // 计算当年到目标月份前一个月的天数 int days_in_month[] = {31,28,31,30,31,30,31,31,30,31,30,31}; if (is_leap(target_year)) { days_in_month[1] = 29; } for (int m = 0; m < tm->tm_mon; m++) { total_seconds += days_in_month[m] * 24LL * 3600LL; } // 加上当月的天数(tm_mday从1开始,所以减1) total_seconds += (tm->tm_mday - 1) * 24LL * 3600LL; // 加上时分秒 total_seconds += tm->tm_hour * 3600LL + tm->tm_min * 60LL + tm->tm_sec; return total_seconds; } // 你的核心函数修改为使用自定义my_timegm time_t getEpochTimeAtSOY(){ time_t currTime = time(NULL); struct tm utc_tm{}; // 用gmtime_r获取UTC时间,避免本地时区影响(优先用线程安全版本) if (gmtime_r(&currTime, &utc_tm) == nullptr) { return -1; } struct tm t; memset(&t, 0, sizeof(t)); t.tm_year = utc_tm.tm_year; t.tm_mday = 1; return my_timegm(&t); }
这个方案完全不依赖任何平台特定函数,适配所有嵌入式和桌面平台,唯一需要注意的是用gmtime_r替代gmtime(如果平台支持),避免线程安全问题。
内容的提问来源于stack exchange,提问作者Aditya Sharma
相关产品推荐
相关产品推荐

