Препроцессор C __TIMESTAMP__ в ISO 8601:2004

Как мне заменить __TIMESTAMP__ в ISO 8601:2004?

__TIMESTAMP__

Sat Jul  6 02:50:06 2013

vs

__TIMESTAMP_ISO__

2013-07-06T00:50:06Z

person Cetin Sert    schedule 06.07.2013    source источник
comment
Не могу понять, почему ребята из команды GCC еще не реализовали это. ISO 8601 существует уже довольно давно... xkcd.com/1179   -  person kebs    schedule 19.07.2018


Ответы (5)


О, оптимист! Вы же не ожидаете, что один стандарт будет обращать внимание на другой, не так ли? Как вы знаете, определения __TIMESTAMP__ нет в стандартном C. Было бы здорово иметь такой формат, как предложенный вами __TIMESTAMP_ISO__ (вы всегда хотите зулусское время или было бы лучше иметь смещение местного часового пояса?), но, честно говоря, самый простой способ добавить его - это патч для GCC и Clang и так далее.

Вы можете попробовать поиграть с asctime(), как это было предложено user1034749 ответ, но я бы не стал этого делать.

В руководстве GCC 4.8.1 есть интересное подавление предупреждений:

-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;
}
person Jonathan Leffler    schedule 06.07.2013
comment
Большое спасибо! Я использую это в своем make-файле: CFLAGS += -D__TIMESTAMP_ISO__=$(shell date -u +'"\"%Y-%m-%dT%H:%M:%SZ\""') прекрасно работает с clang, gcc, icc и т. д. :) - person Cetin Sert; 07.07.2013
comment
Рад, что это работает — так и должно быть. Теоретически вы не должны использовать имя, начинающееся с двойного подчеркивания; они зарезервированы для «реализации». Как я уже отмечал, маловероятно, что вы на самом деле столкнетесь с проблемами, но с тем же успехом вы можете использовать -DTIMESTAMP_ISO=... и быть полностью свободным от риска (столкновения с будущими воплощениями «реализации»). - person Jonathan Leffler; 07.07.2013
comment
:) Я обновлю свое использование, как вы рекомендуете! Кроме того, если я использую -r $(SRC), где SRC — переменная make для текущего скомпилированного файла, как в CFLAGS += -DTIMESTAMP_ISO=$(shell date -r $(SRC) -u +'"\"%Y-%m-%dT%H:%M:%SZ\""'), я точно соответствую исходному поведению __TIMESTAMP__; а именно, дата не меняется при каждой сборке, если исходный файл не был изменен до сборки. - person Cetin Sert; 08.07.2013
comment
Очень хорошее объяснение, поэтому +1. Однако обратите внимание, что этот хак непереносимый, работает только на вариантах Unix. Он опирается на команду date и оболочку bash. - person Laryx Decidua; 13.03.2014
comment
@CetinSert .. но не работает с ccache внутри кэшированного каталога докера :) - person Tõnu Samuel; 10.11.2020

Ответ @Jonathan Leffler предоставляет отличное решение, но может быть одна вещь, которая была проигнорирована:
Исходный вопрос задавал альтернативный формат __TIMESTAMP__, в то время как __TIMESTAMP__ должен быть расширен до строки последней измененной даты и времени. текущего исходного файла. Однако первоначальный ответ фактически назначал __TIMESTAMP__ дате-времени запуска gcc (т. е. время сборки), которое работает как __DATE__ и __TIME__.

Как улучшить:
с помощью @mmond ответ, заменяет команду date +'"%Y-%m-%dT%H:%M:%S"' в -D__TIMESTAMP__=... на date +'"%Y-%m-%dT%H:%M:%S"' -r xx.c, то есть добавляет опцию -r xx.c для ссылки на xx.c при выполнении команды date. xx.c — это создаваемый исходный код.

Ссылка:
согласно GCC [manual][2]

__TIMESTAMP__
Этот макрос преобразуется в строковую константу, описывающую дату и время последней модификации текущего исходного файла.

person Yingyu YOU    schedule 29.06.2018

Вот способ определить строковую константу, которая содержит дату в отметке времени компиляции. Не составит труда добавить к нему временную часть и заставить работать с __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);
person dc42    schedule 06.11.2020

Если вам нужен действительно кросс-платформенный способ форматирования строки отметки времени компиляции в формате ISO 8601 или любом другом формате, определенном во время компиляции, вместо этого вы можете рассмотреть возможность использования CMake (который всегда полезно использовать).

То, что вы хотите, можно легко выполнить с помощью CMake.

person dbanet    schedule 14.07.2015

clang и gcc использовали для этой цели функцию C "asctime", я полагаю, что icc также использует ее. В Linux вы можете использовать LD_PRELOAD, чтобы поймать вызов asctime и заменить его любой строкой, которую вы хотите.

person fghj    schedule 06.07.2013