【问题标题】:C preprocessor __TIMESTAMP__ in ISO 8601:2004ISO 8601:2004 中的 C 预处理器 __TIMESTAMP__
【发布时间】:2013-07-04 03:04:03
【问题描述】:

如何在 ISO 8601:2004 中替换 __TIMESTAMP__

__TIMESTAMP__

Sat Jul  6 02:50:06 2013

__TIMESTAMP_ISO__

2013-07-06T00:50:06Z

【问题讨论】:

  • 无法理解为什么 GCC 团队的人还没有实现它。 ISO 8601 已经存在很长时间了...xkcd.com/1179

标签: c gcc clang c-preprocessor icc


【解决方案1】:

哦,你这个乐观主义者!您不会真的期望一个标准关注另一个标准,对吗?正如您所知,__TIMESTAMP__ 定义不在标准 C 中。有一个像你提议的__TIMESTAMP_ISO__ 这样的格式会很棒(你总是想要祖鲁时间,还是让本地时区偏移更好?),但坦率地说,添加它的最简单方法可能是GCC 和 Clang 等的补丁。

您可以按照user1034749answer 的建议尝试使用asctime() 进行胡闹,但我不想那样做。

在 GCC 4.8.1 manual 中,有一个有趣的警告抑制:

-Wno-builtin-macro-redefined
如果某些内置宏被重新定义,则不要发出警告。这会抑制对 __TIMESTAMP____TIME____DATE____FILE____BASE_FILE__.

这建议您可以尝试:

gcc ... -Wno-builtin-macro-redefined -D__TIMESTAMP__=$(date +'"%Y-%m-%dT%H:%M:%S"') ...

(注意从date 获取字符串所需的象形文字,用双引号括起来。)但是,一些早期版本的 GCC 不支持该选项;我不记得以前见过它。你仍然可以重新定义__TIMESTAMP__

$ gcc -std=c99   -Wall -Wextra  -O xx.c -o xx
$ ./xx 
Fri Jul  5 19:56:25 2013
$ gcc -std=c99 -Wall -Wextra -D__TIMESTAMP__=$(date +'"%Y-%m-%dT%H:%M:%S"') -O xx.c -o xx  
<command-line>: warning: "__TIMESTAMP__" redefined
$ ./xx
2013-07-05T20:10:28
$

不是很漂亮,但它可以工作......哦,只是为了记录,源代码是(微不足道的):

#include <stdio.h>

int main(void)
{
    printf("%s\n", __TIMESTAMP__);
    return 0;
}

【讨论】:

  • 非常感谢!我在我的 makefile 中使用它:CFLAGS += -D__TIMESTAMP_ISO__=$(shell date -u +'"\"%Y-%m-%dT%H:%M:%SZ\""') 与 clang、gcc、icc 等一起使用就像一个魅力:)
  • 很高兴它有效 - 它应该。从理论上讲,您不应该使用以双下划线开头的名称;它们保留用于“实施”。正如我所指出的,您实际上不太可能遇到问题,但您同样可以使用 -DTIMESTAMP_ISO=... 并完全避免风险(与“实施”的未来化身发生冲突)。
  • :) 我会按照您的建议更新我的用法!另外,如果我使用-r $(SRC),其中SRC 是当前编译文件的make 变量,如CFLAGS += -DTIMESTAMP_ISO=$(shell date -r $(SRC) -u +'"\"%Y-%m-%dT%H:%M:%SZ\""'),我会精确匹配原始__TIMESTAMP__ 行为;也就是说,除非在构建之前修改了源文件,否则每次构建的日期都不会改变。
  • 非常好的解释,因此 +1。但是,请注意,此 hack 是不可移植,仅适用于 Unix 变体。它依赖于date 命令和bash shell。
  • @user465139:非 C 代码假定具有 POSIX date 命令和可识别 $(…) 以进行命令替换的 POSIX 兼容 shell 的 POSIX 环境。引用也依赖于 POSIX shell。对于 Windows 环境,我可能会编写一个 C 程序以在诸如 timestamp.h 之类的文件中生成 #define TIMESTAMP_ISO "2014-09-28T12:00:03",然后在正在编译的代码中生成 #include "timestamp.h"。当然,这也适用于 POSIX 系统。
【解决方案2】:

@Jonathan Leffler 的回答提供了一个很好的解决方案,但可能会忽略一件事:
最初的问题是询问__TIMESTAMP__ 的替代格式,而__TIMESTAMP__ 将被扩展为当前源文件的最后一个modified 日期时间字符串。 然而,最初的答案实际上是为__TIMESTAMP__ 分配运行 gcc 的日期时间(即 building 时间),它作为__DATE____TIME__ 工作。

如何改进
使用@mmond 的answer,将-D__TIMESTAMP__=... 中的date +'"%Y-%m-%dT%H:%M:%S"' 命令替换为date +'"%Y-%m-%dT%H:%M:%S"' -r xx.c,即在运行date 命令时添加选项-r xx.c 以引用xx.cxx.c 是正在构建的源代码。

参考:
根据 GCC 的[manual][2]

__TIMESTAMP__
该宏扩展为一个字符串常量,描述了当前源文件最后一次修改的日期和时间。

【讨论】:

    【解决方案3】:

    这是一种定义字符串常量的方法,该常量包含编译时间戳的日期部分。将时间部分添加到其中并不难,并使其从__TIMESTAMP__ 而不是__DATE__ 工作。

    constexpr unsigned int compileYear = (__DATE__[7] - '0') * 1000 + (__DATE__[8] - '0') * 100 + (__DATE__[9] - '0') * 10 + (__DATE__[10] - '0');
    constexpr unsigned int compileMonth = (__DATE__[0] == 'J') ? ((__DATE__[1] == 'a') ? 1 : ((__DATE__[2] == 'n') ? 6 : 7))    // Jan, Jun or Jul
                                    : (__DATE__[0] == 'F') ? 2                                                              // Feb
                                    : (__DATE__[0] == 'M') ? ((__DATE__[2] == 'r') ? 3 : 5)                                 // Mar or May
                                    : (__DATE__[0] == 'A') ? ((__DATE__[2] == 'p') ? 4 : 8)                                 // Apr or Aug
                                    : (__DATE__[0] == 'S') ? 9                                                              // Sep
                                    : (__DATE__[0] == 'O') ? 10                                                             // Oct
                                    : (__DATE__[0] == 'N') ? 11                                                             // Nov
                                    : (__DATE__[0] == 'D') ? 12                                                             // Dec
                                    : 0;
    constexpr unsigned int compileDay = (__DATE__[4] == ' ') ? (__DATE__[5] - '0') : (__DATE__[4] - '0') * 10 + (__DATE__[5] - '0');
    
    constexpr char IsoDate[] =
    {   compileYear/1000 + '0', (compileYear % 1000)/100 + '0', (compileYear % 100)/10 + '0', compileYear % 10 + '0',
        '-',  compileMonth/10 + '0', compileMonth%10 + '0',
        '-',  compileDay/10 + '0', compileDay%10 + '0',
        0
    };
    
    // Test that it gets it right today
    #include <cstring>
    static_assert(strcmp(IsoDate, "2020-11-06") == 0);
    

    【讨论】:

      【解决方案4】:

      如果您需要一种真正跨平台的方式来将编译时间戳字符串格式化为 ISO 8601 或在编译时定义的任何其他格式,您可以考虑使用CMake(考虑使用总是好的)。

      你想要什么can be easily accomplished with CMake

      【讨论】:

        【解决方案5】:

        clang 和 gcc 为此使用了 C 函数“asctime”,我想 icc 也使用它。在 Linux 上,您可以使用 LD_PRELOAD 来捕获 asctime 调用并替换为您想要的任何字符串。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2011-01-10
          • 1970-01-01
          • 2010-09-07
          • 1970-01-01
          • 2011-07-02
          • 2011-06-28
          • 1970-01-01
          相关资源
          最近更新 更多