<p>标准C++无运行时获取编译时间API,须用预处理器宏DATE和TIME在编译时固化时间字符串,推荐定义为inline constexpr const char* BUILD_TIME = DATE " " TIME;</p>

编译时用 __DATE__ 和 __TIME__ 宏生成字符串
标准 C++ 没有运行时获取“编译时间”的 API,必须靠预处理器在编译那一刻把时间固化进二进制。最直接的方式是用内置宏:__DATE__(格式如 "Jan 15 2024")和 __TIME__(格式如 "14:32:18")。它们在每次编译时被替换成字面量字符串,不依赖系统时钟,也不受运行环境影响。
常见错误是试图在运行时调用函数生成它——不行,这些宏只在预处理阶段展开;也别试图用 time() 或 __FILE__ 替代,前者返回运行时间,后者不含时间信息。
- 推荐定义为
constexpr字符串,避免运行时拷贝:constexpr const char* BUILD_DATE = __DATE__;
- 拼接日期和时间时注意空格和格式一致性,例如:
constexpr const char* BUILD_DATETIME = __DATE__ " " __TIME__;
- 不同编译器对
__DATE__/__TIME__的格式无强制标准,但 GCC、Clang、MSVC 都遵循"MMM DD YYYY"和"HH:MM:SS"格式,可安全假设
用 __TIMESTAMP__ 获取更精确的编译时间戳
__TIMESTAMP__ 是 GCC/Clang 扩展宏(MSVC 不支持),值为类似 "Wed Jan 15 14:32:18 2024" 的完整字符串,带星期、时区无关的本地时间,精度到秒,且比拼接 __DATE__+__TIME__ 更紧凑。
但它不是标准 C++,跨平台项目需加条件编译:
立即学习“C++免费学习笔记(深入)”;
#ifdef __GNUC__ constexpr const char* BUILD_TIMESTAMP = __TIMESTAMP__; #else constexpr const char* BUILD_TIMESTAMP = __DATE__ " " __TIME__; #endif
- 注意:如果源文件没改动,某些构建系统(如 Ninja + CMake)可能跳过重编译,
__TIMESTAMP__不会更新——它反映的是该源文件最后一次编译时间,不是链接时间 - 不能用于判断“是否重新构建”,仅作信息记录;真要验证构建新鲜度,得靠构建系统注入版本号或哈希
避免把编译时间硬编码进字符串常量导致的链接问题
如果多个 .cpp 文件都定义了同名的 constexpr const char* BUILD_TIME,且未声明为 inline 或 static,链接时可能报 duplicate symbol 错误(尤其启用 LTO 或多个 TU 引用同一头文件时)。
- 头文件中定义必须加
inline(C++17 起):inline constexpr const char* BUILD_TIME = __DATE__ " " __TIME__;
- 或改用
static局部变量(C++11 起):static constexpr const char* build_time = __DATE__ " " __TIME__;
- 更稳妥的做法是只在一个 .cpp 里定义 extern 声明,在头文件中用
extern const char* BUILD_TIME;,避免重复实例化
运行时想读取可执行文件的修改时间?那是另一回事
有人混淆“编译时间”和“可执行文件磁盘上的 mtime”。后者可用 stat()(POSIX)或 GetFileTime()(Windows)读取,但它反映的是文件最后写入时间——可能被复制、打包、签名覆盖,和实际编译时间不一致。
- Linux/macOS 示例:
struct stat st; if (stat("/proc/self/exe", &st) == 0) { /* st.st_mtime */ } - Windows 示例:
HANDLE h = CreateFile(L"\\?\path\to\exe", ...); GetFileTime(h, ..., ..., ...);
- 这个时间不可靠:CI 构建后重命名文件、容器镜像解压、杀毒软件扫描都会改
mtime;而__DATE__是铁板钉钉的编译时刻
真正要的是编译时间,就别碰文件系统时间戳——它解决的是另一个问题,而且容易出错。


















