C/C++:如何获取构建时间的整数unix时间戳(非字符串)

btxsgosb  于 2023-03-05  发布在  C/C++
关注(0)|答案(9)|浏览(222)

我在努力实现一件微不足道的事情:我需要存储构建时间的整数32位unix时间戳,但我找到的所有宏(__DATE____TIME____TIMESTAMP__)都扩展为字符串,而不是整数。
看起来,我们只是没有它(这对我来说很奇怪)。我真的希望有整数,而不是字符串。
有哪些最佳实践可以实现这一目标?

    • 统一采购司:**

作为旁注:我做嵌入式的东西,所以我没有足够的资源(比如说,128KB的闪存),因此解析字符串真的是个坏主意。
我为什么需要它:我只需要有每个测试版版本号唯一。首先,十六进制文件将命名为my-firmware-v2-33-BETA-1397315745.hex,其次,当我需要在设备的屏幕上显示当前版本,我可能要回显它在各种格式。

w8rqjzmb

w8rqjzmb1#

所以,今天晚上我玩得很开心,用宏创建了一个头文件来生成UNIX时间戳,没有任何外部程序或特殊的编译器特性!只需包含头文件并使用__TIME_UNIX__宏。
实际上代码非常简单:
1.字符串中的数字字符转换为数字,str[i]-'0'为 * loreb * 建议值,并根据其位置进行加权。
1.月份字符串的处理类似于 * chux * answer;逐个检查字符,并用? :算子将其作为一个组来评估。
1.计算最近几个月的天数是使用交换表达式? :完成的。
1.闰年计算很简单,因为在UNIX时间框架内每4年插入一个闰日
1.最后,所有单个值都用各自的秒数加权,得到UNIX时间。注意,SEC_PER_DAY必须减去一次,因为JAN 01 1970, 00:00:00必须是0
代码已在ATMEL Studio 7(Visual Studio 2015)中使用默认编译器和设置(avr-gcc、-O1优化)进行了测试,并通过检查生成的. lss文件确认了结果。
将下面的代码复制粘贴到一个头文件中,并将其包含在任何需要的地方。

/*
 * compile_time.h
 *
 * Created: 30.05.2017 20:57:58
 *  Author: Dennis (instructable.com/member/nqtronix)
 *
 * This code provides the macro __TIME_UNIX__ which returns the current time in UNIX format. It can
 * be used to identify a version of code on an embedded device, to initialize its RTC and much more.
 * Along that several more constants for seconds, minutes, etc. are provided
 *
 * The macro is based on __TIME__ and __DATE__, which are assumed to be formatted "HH:MM:SS" and
 * "MMM DD YYYY", respectively. The actual value can be calculated by the C compiler at compile time
 * as all inputs are literals. MAKE SURE TO ENABLE OPTIMISATION!
 */ 

#ifndef COMPILE_TIME_H_
#define COMPILE_TIME_H_

// extracts 1..4 characters from a string and interprets it as a decimal value
#define CONV_STR2DEC_1(str, i)  (str[i]>'0'?str[i]-'0':0)
#define CONV_STR2DEC_2(str, i)  (CONV_STR2DEC_1(str, i)*10 + str[i+1]-'0')
#define CONV_STR2DEC_3(str, i)  (CONV_STR2DEC_2(str, i)*10 + str[i+2]-'0')
#define CONV_STR2DEC_4(str, i)  (CONV_STR2DEC_3(str, i)*10 + str[i+3]-'0')

// Some definitions for calculation
#define SEC_PER_MIN             60UL
#define SEC_PER_HOUR            3600UL
#define SEC_PER_DAY             86400UL
#define SEC_PER_YEAR            (SEC_PER_DAY*365)
#define UNIX_START_YEAR         1970UL

// Custom "glue logic" to convert the month name to a usable number
#define GET_MONTH(str, i)      (str[i]=='J' && str[i+1]=='a' && str[i+2]=='n' ? 1 :     \
                                str[i]=='F' && str[i+1]=='e' && str[i+2]=='b' ? 2 :     \
                                str[i]=='M' && str[i+1]=='a' && str[i+2]=='r' ? 3 :     \
                                str[i]=='A' && str[i+1]=='p' && str[i+2]=='r' ? 4 :     \
                                str[i]=='M' && str[i+1]=='a' && str[i+2]=='y' ? 5 :     \
                                str[i]=='J' && str[i+1]=='u' && str[i+2]=='n' ? 6 :     \
                                str[i]=='J' && str[i+1]=='u' && str[i+2]=='l' ? 7 :     \
                                str[i]=='A' && str[i+1]=='u' && str[i+2]=='g' ? 8 :     \
                                str[i]=='S' && str[i+1]=='e' && str[i+2]=='p' ? 9 :     \
                                str[i]=='O' && str[i+1]=='c' && str[i+2]=='t' ? 10 :    \
                                str[i]=='N' && str[i+1]=='o' && str[i+2]=='v' ? 11 :    \
                                str[i]=='D' && str[i+1]=='e' && str[i+2]=='c' ? 12 : 0)

#define GET_MONTH2DAYS(month)  ((month == 1 ? 0 : 31 +                      \
                                (month == 2 ? 0 : 28 +                      \
                                (month == 3 ? 0 : 31 +                      \
                                (month == 4 ? 0 : 30 +                      \
                                (month == 5 ? 0 : 31 +                      \
                                (month == 6 ? 0 : 30 +                      \
                                (month == 7 ? 0 : 31 +                      \
                                (month == 8 ? 0 : 31 +                      \
                                (month == 9 ? 0 : 30 +                      \
                                (month == 10 ? 0 : 31 +                     \
                                (month == 11 ? 0 : 30))))))))))))           \

#define GET_LEAP_DAYS           ((__TIME_YEARS__-1968)/4 - (__TIME_MONTH__ <=2 ? 1 : 0))


#define __TIME_SECONDS__        CONV_STR2DEC_2(__TIME__, 6)
#define __TIME_MINUTES__        CONV_STR2DEC_2(__TIME__, 3)
#define __TIME_HOURS__          CONV_STR2DEC_2(__TIME__, 0)
#define __TIME_DAYS__           CONV_STR2DEC_2(__DATE__, 4)
#define __TIME_MONTH__          GET_MONTH(__DATE__, 0)
#define __TIME_YEARS__          CONV_STR2DEC_4(__DATE__, 7)

#define __TIME_UNIX__         ((__TIME_YEARS__-UNIX_START_YEAR)*SEC_PER_YEAR+       \
                                GET_LEAP_DAYS*SEC_PER_DAY+                          \
                                GET_MONTH2DAYS(__TIME_MONTH__)*SEC_PER_DAY+         \
                                __TIME_DAYS__*SEC_PER_DAY-SEC_PER_DAY+              \
                                __TIME_HOURS__*SEC_PER_HOUR+                        \
                                __TIME_MINUTES__*SEC_PER_MIN+                       \
                                __TIME_SECONDS__)

#endif /* COMPILE_TIME_H_ */
    • 编辑:**

初始版本没有考虑100和400年模对2月天数的影响,这在2001年和2101年之间应该不是问题,但这里有一个更通用的宏:

/* 
 *
 * Created: 29.03.2018
 *
 * Authors:
 * 
 * Assembled from the code released on Stackoverflow by:
 *   Dennis (instructable.com/member/nqtronix)    |   https://stackoverflow.com/questions/23032002/c-c-how-to-get-integer-unix-timestamp-of-build-time-not-string
 * and
 *   Alexis Wilke                                 |   https://stackoverflow.com/questions/10538444/do-you-know-of-a-c-macro-to-compute-unix-time-and-date
 *
 * Assembled by Jean Rabault
 * 
 * UNIX_TIMESTAMP gives the UNIX timestamp (unsigned long integer of seconds since 1st Jan 1970) of compilation from macros using the compiler defined __TIME__ macro.
 * This should include Gregorian calendar leap days, in particular the 29ths of February, 100 and 400 years modulo leaps.
 * 
 * Careful: __TIME__ is the local time of the computer, NOT the UTC time in general!
 * 
 */

#ifndef COMPILE_TIME_H_
#define COMPILE_TIME_H_

// Some definitions for calculation
#define SEC_PER_MIN             60UL
#define SEC_PER_HOUR            3600UL
#define SEC_PER_DAY             86400UL
#define SEC_PER_YEAR            (SEC_PER_DAY*365)

// extracts 1..4 characters from a string and interprets it as a decimal value
#define CONV_STR2DEC_1(str, i)  (str[i]>'0'?str[i]-'0':0)
#define CONV_STR2DEC_2(str, i)  (CONV_STR2DEC_1(str, i)*10 + str[i+1]-'0')
#define CONV_STR2DEC_3(str, i)  (CONV_STR2DEC_2(str, i)*10 + str[i+2]-'0')
#define CONV_STR2DEC_4(str, i)  (CONV_STR2DEC_3(str, i)*10 + str[i+3]-'0')

// Custom "glue logic" to convert the month name to a usable number
#define GET_MONTH(str, i)      (str[i]=='J' && str[i+1]=='a' && str[i+2]=='n' ? 1 :     \
                                str[i]=='F' && str[i+1]=='e' && str[i+2]=='b' ? 2 :     \
                                str[i]=='M' && str[i+1]=='a' && str[i+2]=='r' ? 3 :     \
                                str[i]=='A' && str[i+1]=='p' && str[i+2]=='r' ? 4 :     \
                                str[i]=='M' && str[i+1]=='a' && str[i+2]=='y' ? 5 :     \
                                str[i]=='J' && str[i+1]=='u' && str[i+2]=='n' ? 6 :     \
                                str[i]=='J' && str[i+1]=='u' && str[i+2]=='l' ? 7 :     \
                                str[i]=='A' && str[i+1]=='u' && str[i+2]=='g' ? 8 :     \
                                str[i]=='S' && str[i+1]=='e' && str[i+2]=='p' ? 9 :     \
                                str[i]=='O' && str[i+1]=='c' && str[i+2]=='t' ? 10 :    \
                                str[i]=='N' && str[i+1]=='o' && str[i+2]=='v' ? 11 :    \
                                str[i]=='D' && str[i+1]=='e' && str[i+2]=='c' ? 12 : 0)

// extract the information from the time string given by __TIME__ and __DATE__
#define __TIME_SECONDS__        CONV_STR2DEC_2(__TIME__, 6)
#define __TIME_MINUTES__        CONV_STR2DEC_2(__TIME__, 3)
#define __TIME_HOURS__          CONV_STR2DEC_2(__TIME__, 0)
#define __TIME_DAYS__           CONV_STR2DEC_2(__DATE__, 4)
#define __TIME_MONTH__          GET_MONTH(__DATE__, 0)
#define __TIME_YEARS__          CONV_STR2DEC_4(__DATE__, 7)

// Days in February
#define _UNIX_TIMESTAMP_FDAY(year) \
    (((year) % 400) == 0UL ? 29UL : \
        (((year) % 100) == 0UL ? 28UL : \
            (((year) % 4) == 0UL ? 29UL : \
                28UL)))

// Days in the year
#define _UNIX_TIMESTAMP_YDAY(year, month, day) \
    ( \
        /* January */    day \
        /* February */ + (month >=  2 ? 31UL : 0UL) \
        /* March */    + (month >=  3 ? _UNIX_TIMESTAMP_FDAY(year) : 0UL) \
        /* April */    + (month >=  4 ? 31UL : 0UL) \
        /* May */      + (month >=  5 ? 30UL : 0UL) \
        /* June */     + (month >=  6 ? 31UL : 0UL) \
        /* July */     + (month >=  7 ? 30UL : 0UL) \
        /* August */   + (month >=  8 ? 31UL : 0UL) \
        /* September */+ (month >=  9 ? 31UL : 0UL) \
        /* October */  + (month >= 10 ? 30UL : 0UL) \
        /* November */ + (month >= 11 ? 31UL : 0UL) \
        /* December */ + (month >= 12 ? 30UL : 0UL) \
    )

// get the UNIX timestamp from a digits representation
#define _UNIX_TIMESTAMP(year, month, day, hour, minute, second) \
    ( /* time */ second \
                + minute * SEC_PER_MIN \
                + hour * SEC_PER_HOUR \
    + /* year day (month + day) */ (_UNIX_TIMESTAMP_YDAY(year, month, day) - 1) * SEC_PER_DAY \
    + /* year */ (year - 1970UL) * SEC_PER_YEAR \
                + ((year - 1969UL) / 4UL) * SEC_PER_DAY \
                - ((year - 1901UL) / 100UL) * SEC_PER_DAY \
                + ((year - 1601UL) / 400UL) * SEC_PER_DAY \
    )

// the UNIX timestamp
#define UNIX_TIMESTAMP (_UNIX_TIMESTAMP(__TIME_YEARS__, __TIME_MONTH__, __TIME_DAYS__, __TIME_HOURS__, __TIME_MINUTES__, __TIME_SECONDS__))

#endif
mpbci0fu

mpbci0fu2#

您可以在每次构建时生成一个timestamp.h文件作为预构建步骤,并将该文件包含在您的源代码中。我不知道您使用的是什么构建工具(嵌入式世界非常广泛),但迄今为止我看到的每一个构建工具都允许用户定义自定义预构建和构建后步骤(Freescale CodeWarrior、AVR Studio、MSVS...)。
例如,在Windows上的AVR Studio中,我使用了此预构建步骤(请注意,$(SolutionDir)是特定于AVR stuio的,它基于MSVS,您可以替换为您需要的任何文件系统路径):

FOR /F %%A IN ('C:\cygwin\bin\date +%s') DO SET BUILD_TIMESTAMP=%%A
echo #define BUILD_TIME %BUILD_TIMESTAMP% > "$(SolutionDir)timestamp.h"

在项目的一个C文件中,我通常会包含这个生成的文件(您到该文件的路径可能不同...):

#include "../timestamp.h"

生成的文件如下所示:

#define BUILD_TIME 1397317498

因此,当我单击“构建项目”时,工作室首先运行我的命令,生成新的timestamp.h,然后将其用作任何其他C文件中的include。
请注意,上面的例子使用Cygwin(C:\cygwin\bin\date +%s)来获取时间戳。如果您不想使用Cygwin,您将不得不找到一些其他的方法来让Windows为您生成时间戳。您可以编写自己的命令行实用程序(它应该是大约10行代码的C:-)或搜索互联网的一些其他方法。

bbmckpt7

bbmckpt73#

为什么不在命令行中定义它呢?

gcc -DCOMPILE_TIME=`date '+%s'` mysources.c
62lalag4

62lalag44#

/**
 * @brief  Macros to get integer build timestamp:
 *         __DATE_TIME_Y2K__  seconds since 2000-01-01,00:00:00
 *         __DATE_TIME_UNIX__ seconds since 1970-01-01 00:00:00
 *
 *           01234567890              01234567
 * __DATE__ "Jul 27 2019"   __TIME__ "12:34:56"
 */
#ifndef __DATE_TIME_H__
#define __DATE_TIME_H__

// For testing
//#define __DATE__ ("Jan 01 2000")
//#define __TIME__ ("00:00:00")

#define Y2K_UNIX_EPOCH_DIFF 946684800U
#define YEARS ((__DATE__[10] - '0' + (__DATE__[9] - '0') * 10))
#define DAY_OF_MONTH ((__DATE__[5] - '0') \
                  + (((__DATE__[4] > '0')? __DATE__[4] - '0': 0) * 10) - 1)
#define DAY_OF_YEAR ((DAY_OF_MONTH) + \
( /* Jan */ (__DATE__[0] == 'J' && __DATE__[1] == 'a')?   0: \
  /* Feb */ (__DATE__[0] == 'F'                      )?  31: \
  /* Mar */ (__DATE__[0] == 'M' && __DATE__[2] == 'r')?  59: \
  /* Apr */ (__DATE__[0] == 'A' && __DATE__[1] == 'p')?  90: \
  /* May */ (__DATE__[0] == 'M'                      )? 120: \
  /* Jun */ (__DATE__[0] == 'J' && __DATE__[2] == 'n')? 151: \
  /* Jul */ (__DATE__[0] == 'J'                      )? 181: \
  /* Aug */ (__DATE__[0] == 'A'                      )? 212: \
  /* Sep */ (__DATE__[0] == 'S'                      )? 243: \
  /* Oct */ (__DATE__[0] == 'O'                      )? 273: \
  /* Nov */ (__DATE__[0] == 'N'                      )? 304: \
  /* Dec */                                             334  ))
#define LEAP_DAYS (YEARS / 4 + ((YEARS % 4 == 0 && DAY_OF_YEAR > 58)? 1 : 0) )      
#define __DATE_TIME_Y2K__ ( (YEARS * 365 + LEAP_DAYS + DAY_OF_YEAR ) * 86400 \
                    + ((__TIME__[0] - '0') * 10 + __TIME__[1] - '0') * 3600 \
                    + ((__TIME__[3] - '0') * 10 + __TIME__[4] - '0') * 60 \
                    + ((__TIME__[6] - '0') * 10 + __TIME__[7] - '0') )
#define  __DATE_TIME_UNIX__ ( __DATE_TIME_Y2K__ + Y2K_UNIX_EPOCH_DIFF )
#endif /* __DATE_TIME_H__ */

这只是一个更紧密,更轻的版本开发的@nqtronix的版本以上。享受!
注意:此宏忽略世纪,因此仅在2000-01- 01,00:00:00和2099-12- 31,23:59:59之间有效。

sg3maiej

sg3maiej5#

看看下面的暴行:

#include <stdio.h>
#define dec(ch) ((ch)-'0')
#define t(index, multiplier)    (dec(__TIME__[index]) * (multiplier))
/* only minutes and seconds - you get the idea */
#define mmss()  (t(3,600) + t(4,60) + t(6,10) + t(7,1))
int main()
{
        /*
        int i;
        printf("time = %s\n", __TIME__);
        for(i=0; __TIME__[i]; i++)
                printf("t(%d) = %d\n", i, t(i,1));
        */
        printf("mmss = %d\n", mmss());
        return 0;
}

在我的计算机上gcc -O能够将其优化为一个常数值;要获得完整的time_t,您需要一个__DATE__的兄弟宏,并查看生成的程序集。
如果有人问起,就说这不是我写的;)
编辑:为了清楚起见,您可能应该按照Roman Hocke的答案编写一个简短的C程序来为您生成值(当然,如果您正在进行交叉编译,您应该小心一点...)

w6mmgewl

w6mmgewl6#

编辑:根据@ Lu Vnh Phúc的评论,似乎关键的想法需要解释:* 所有 * 计算都在 * 编译 * 时完成。参见下面生成的OP代码。
下面的代码不是生成time_t,而是在编译器的时区中生成struct tm。如果需要,调用mktime()将返回time_t

time_t CompileTime = mktime(CompileTimeTM());
printf("%lld\n", (long long) CompileTime);

在其他应用程序中,不是返回struct tm,而是每个字段赋值简单地打印值以显示版本。

else if ((d[3]=='M') && (d[4]=='a') && (d[5]=='y')) Print2Dig(5);

下面的冗长代码在PIC编译器中生成的指令很少,因为它优化了很多。类似的方法可以使用__TIMESTAMP__

// Dummy: used to detect a bad date parsing in Send_ID()
extern void BadDateM(void);

struct tm *CompileTimeTM(void) {
  static const char d[10] = __DATE__;
  static const char t[10] = __TIME__;
  static struct tm ct;

  ct.tm_year = (d[7]-'0')*10 + (d[8]-'0') + 2000 - 1900;

  #IGNORE_WARNINGS  204
  if (0) ;
  else if ((d[3]=='J') && (d[4]=='a') && (d[5]=='n')) ct.tm_mon = 1-1;
  else if ((d[3]=='F') && (d[4]=='e') && (d[5]=='b')) ct.tm_mon = 2-1;
  else if ((d[3]=='M') && (d[4]=='a') && (d[5]=='r')) ct.tm_mon = 3-1;
  else if ((d[3]=='A') && (d[4]=='p') && (d[5]=='r')) ct.tm_mon = 4-1;
  else if ((d[3]=='M') && (d[4]=='a') && (d[5]=='y')) ct.tm_mon = 5-1;
  else if ((d[3]=='J') && (d[4]=='u') && (d[5]=='n')) ct.tm_mon = 6-1;
  else if ((d[3]=='J') && (d[4]=='u') && (d[5]=='l')) ct.tm_mon = 7-1;
  else if ((d[3]=='A') && (d[4]=='u') && (d[5]=='g')) ct.tm_mon = 8-1;
  else if ((d[3]=='S') && (d[4]=='e') && (d[5]=='p')) ct.tm_mon = 9-1;
  else if ((d[3]=='O') && (d[4]=='c') && (d[5]=='t')) ct.tm_mon = 10-1;
  else if ((d[3]=='N') && (d[4]=='o') && (d[5]=='v')) ct.tm_mon = 11-1;
  else if ((d[3]=='D') && (d[4]=='e') && (d[5]=='c')) ct.tm_mon = 12-1;
  else BadDateM(); // compile this if no match above, and thus fail link.
  #IGNORE_WARNINGS  NONE

  ct.tm_mday = (d[0]-'0')*10 + (d[1]-'0');
  ct.tm_hour = (t[0]-'0')*10 + (t[1]-'0');
  ct.tm_min = (t[3]-'0')*10 + (t[4]-'0');
  ct.tm_sec = (t[6]-'0')*10 + (t[7]-'0');

  ct.tm_isdst = -1;  // information is not available.
  // ct.tm_yday = 0;
  // ct.tm_wday = 0;

  return &ct;
  }

列名

struct tm *CompileTimeTM(void) { 
static const char d[10] = __DATE__; 
static const char t[10] = __TIME__; 
static struct tm ct; 

ct.tm_year = (d[7]-'0')*10 + (d[8]-'0') + 2000 - 1900; 
0F78 200724         MOV     #72,W4         : W4 = 72
0F7A 8864D4         MOV     W4,C9A         : C9A = W4

#IGNORE_WARNINGS  204 
if (0) ; 
else if ((d[3]=='J') && (d[4]=='a') && (d[5]=='n')) ct.tm_mon = 1-1; 
else if ((d[3]=='F') && (d[4]=='e') && (d[5]=='b')) ct.tm_mon = 2-1; 
else if ((d[3]=='M') && (d[4]=='a') && (d[5]=='r')) ct.tm_mon = 3-1; 
else if ((d[3]=='A') && (d[4]=='p') && (d[5]=='r')) ct.tm_mon = 4-1; 
0F7C 200034         MOV     #3,W4          : W4 = 3
0F7E 8864C4         MOV     W4,C98         : C98 = W4
else if ((d[3]=='M') && (d[4]=='a') && (d[5]=='y')) ct.tm_mon = 5-1; 
else if ((d[3]=='J') && (d[4]=='u') && (d[5]=='n')) ct.tm_mon = 6-1; 
else if ((d[3]=='J') && (d[4]=='u') && (d[5]=='l')) ct.tm_mon = 7-1; 
else if ((d[3]=='A') && (d[4]=='u') && (d[5]=='g')) ct.tm_mon = 8-1; 
else if ((d[3]=='S') && (d[4]=='e') && (d[5]=='p')) ct.tm_mon = 9-1; 
else if ((d[3]=='O') && (d[4]=='c') && (d[5]=='t')) ct.tm_mon = 10-1; 
else if ((d[3]=='N') && (d[4]=='o') && (d[5]=='v')) ct.tm_mon = 11-1; 
else if ((d[3]=='D') && (d[4]=='e') && (d[5]=='c')) ct.tm_mon = 12-1; 
else BadDateM(); // compile this if no match above, and thus fail link. 
#IGNORE_WARNINGS  NONE 

ct.tm_mday = (d[0]-'0')*10 + (d[1]-'0'); 
0F80 2000E4         MOV     #E,W4          : W4 = E
0F82 8864B4         MOV     W4,C96         : C96 = W4
ct.tm_hour = (t[0]-'0')*10 + (t[1]-'0'); 
0F84 2000B4         MOV     #B,W4          : W4 = B
0F86 8864A4         MOV     W4,C94         : C94 = W4
ct.tm_min = (t[3]-'0')*10 + (t[4]-'0'); 
0F88 200354         MOV     #35,W4         : W4 = 35
0F8A 886494         MOV     W4,C92         : C92 = W4
ct.tm_sec = (t[6]-'0')*10 + (t[7]-'0'); 
0F8C 2000D4         MOV     #D,W4          : W4 = D
0F8E 886484         MOV     W4,C90         : C90 = W4
uajslkp6

uajslkp67#

我发现如果某个月的某一天小于10,则必须添加一个特殊检查,否则将返回一个负数

// special case to handle __DATE__ not inserting leading zero on day of month
       // if Day of month is less than 10 - it inserts a blank character
       // this results in a negative number for tm_mday 

       if(d[4] == ' ')
       {
    	   ct.tm_mday =  d[5]-'0';
       }
       else
       {
    	   ct.tm_mday = (d[4]-'0')*10 + (d[5]-'0');
       }
flseospp

flseospp8#

因为您使用的是C++,所以我认为您可以使用constexpr函数在编译时解析__DATE__ __TIME__字符串。
如:

#include <stdint.h>

struct time_custom_t {
  uint16_t year;
  uint8_t month;
  uint8_t day;
  uint8_t hour;
  uint8_t minute;
  uint8_t second;
};

#define __SECONDS_FROM_1970_TO_2000                                              \
  946684800 ///< Unixtime for 2000-01-01 00:00:00, useful for initialization

constexpr uint16_t conv2d(const char *p)
{
    uint8_t v = 0;
    if ('0' <= *p && *p <= '9')
        v = *p - '0';
    return 10 * v + *++p - '0';
}

constexpr uint16_t date2days(uint16_t y, uint8_t m, uint8_t d) {
  const uint8_t daysInMonth[] = {31, 28, 31, 30, 31, 30,
                                       31, 31, 30, 31, 30};
  if (y >= 2000U)
    y -= 2000U;
  uint16_t days = d;
  for (uint8_t i = 1; i < m; ++i)
    days += daysInMonth[i - 1];
  if (m > 2 && y % 4 == 0)
    ++days;
  return days + 365 * y + (y + 3) / 4 - 1;
}

constexpr uint32_t time2ulong(uint16_t days, uint8_t h, uint8_t m, uint8_t s) {
  return ((days * 24UL + h) * 60 + m) * 60 + s;
}

constexpr time_custom_t getBuildTime(const char *date, const char *time)
{
    time_custom_t dt{};
    dt.year = conv2d(date + 9);
    dt.day = conv2d(date + 4);
    dt.hour = conv2d(time);
    dt.minute = conv2d(time + 3);
    dt.second = conv2d(time + 6);

    // Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec
    switch (date[0])
    {
    case 'J': dt.month = (date[1] == 'a') ? 1 : ((date[2] == 'n') ? 6 : 7); break;
    case 'F': dt.month = 2; break;
    case 'A': dt.month = date[2] == 'r' ? 4 : 8; break;
    case 'M': dt.month = date[2] == 'r' ? 3 : 5; break;
    case 'S': dt.month = 9; break;
    case 'O': dt.month = 10; break;
    case 'N': dt.month = 11; break;
    case 'D': dt.month = 12; break;
    }

    return dt;
}

constexpr uint32_t getBuildTimeAsUnixTime(const char *date, const char *time)
{
    time_custom_t dt = getBuildTime (date, time);
    uint32_t unixTime = 0;
    unixTime = time2ulong(date2days(dt.year, dt.month, dt.day), dt.hour, dt.month, dt.minute) + __SECONDS_FROM_1970_TO_2000;
    return unixTime;
}

我尝试使用GCC和-O3标志,结果如下:
PictureGodBolt

gcuhipw9

gcuhipw99#

对我来说,使用@Potion答案https://stackoverflow.com/a/58314096/5949219,除了DAY_OF_MONTH没有减1,所以它变成:

#define Y2K_UNIX_EPOCH_DIFF 946684800U    
#define YEARS ((__DATE__[10] - '0' + (__DATE__[9] - '0') * 10))
#define DAY_OF_MONTH ((__DATE__[5] - '0') \
                  + (((__DATE__[4] > '0')? __DATE__[4] - '0': 0) * 10))
#define DAY_OF_YEAR ((DAY_OF_MONTH) + \
( (__DATE__[0] == 'J' && __DATE__[1] == 'a')?   0: \
  (__DATE__[0] == 'F'                      )?  31: \
  (__DATE__[0] == 'M' && __DATE__[2] == 'r')?  59: \
  (__DATE__[0] == 'A' && __DATE__[1] == 'p')?  90: \
  (__DATE__[0] == 'M'                      )? 120: \
  (__DATE__[0] == 'J' && __DATE__[2] == 'n')? 151: \
  (__DATE__[0] == 'J'                      )? 181: \
  (__DATE__[0] == 'A'                      )? 212: \
  (__DATE__[0] == 'S'                      )? 243: \
  (__DATE__[0] == 'O'                      )? 273: \
  (__DATE__[0] == 'N'                      )? 304: \
                                              334  ))
#define LEAP_DAYS (YEARS / 4 + ((YEARS % 4 == 0 && DAY_OF_YEAR > 58)? 1 : 0) )      
#define __DATE_TIME_Y2K__ ( (YEARS * 365 + LEAP_DAYS + DAY_OF_YEAR ) * 86400 \
                    + ((__TIME__[0] - '0') * 10 + __TIME__[1] - '0') * 3600 \
                    + ((__TIME__[3] - '0') * 10 + __TIME__[4] - '0') * 60 \
                    + ((__TIME__[6] - '0') * 10 + __TIME__[7] - '0') )
#define  UNIX_TIMESTAMP ( __DATE_TIME_Y2K__ + Y2K_UNIX_EPOCH_DIFF )

相关问题