I'm trying to convert the string produced from the __DATE__
macro into a time_t
. I don't need a full-blown date/time parser, something that only handles the format of the __DATE__
macro 开发者_如何学Cwould be great.
A preprocessor method would be nifty, but a function would work just as well. If it's relevant, I'm using MSVC.
Edit: the corrected function should look something like this:
time_t cvt_TIME(char const *time) {
char s_month[5];
int month, day, year;
struct tm t = {0};
static const char month_names[] = "JanFebMarAprMayJunJulAugSepOctNovDec";
sscanf(time, "%s %d %d", s_month, &day, &year);
month = (strstr(month_names, s_month)-month_names)/3;
t.tm_mon = month;
t.tm_mday = day;
t.tm_year = year - 1900;
t.tm_isdst = -1;
return mktime(&t);
}
Basing on the description given at gcc.gnu.org the build date can be obtain at the compilation time using following macros.
#define BUILDTM_YEAR (\
__DATE__[7] == '?' ? 1900 \
: (((__DATE__[7] - '0') * 1000 ) \
+ (__DATE__[8] - '0') * 100 \
+ (__DATE__[9] - '0') * 10 \
+ __DATE__[10] - '0'))
#define BUILDTM_MONTH (\
__DATE__ [2] == '?' ? 1 \
: __DATE__ [2] == 'n' ? (__DATE__ [1] == 'a' ? 1 : 6) \
: __DATE__ [2] == 'b' ? 2 \
: __DATE__ [2] == 'r' ? (__DATE__ [0] == 'M' ? 3 : 4) \
: __DATE__ [2] == 'y' ? 5 \
: __DATE__ [2] == 'l' ? 7 \
: __DATE__ [2] == 'g' ? 8 \
: __DATE__ [2] == 'p' ? 9 \
: __DATE__ [2] == 't' ? 10 \
: __DATE__ [2] == 'v' ? 11 \
: 12)
#define BUILDTM_DAY (\
__DATE__[4] == '?' ? 1 \
: ((__DATE__[4] == ' ' ? 0 : \
((__DATE__[4] - '0') * 10)) + __DATE__[5] - '0'))
I don't know if any other Arduino hackers will stumble upon this question, but I found @JerryCoffin's answer to be quite helpful in solving this problem for my project. Here is a complete example you can paste into Arduino. It uses the Time lib referenced here.
#include "Arduino.h"
#include <Time.h>
#include <stdio.h>
time_t cvt_date(char const *date) {
char s_month[5];
int month, day, year;
tmElements_t tmel;
static const char month_names[] = "JanFebMarAprMayJunJulAugSepOctNovDec";
sscanf(date, "%s %d %d", s_month, &day, &year);
month = (strstr(month_names, s_month)-month_names)/3+1;
tmel.Hour = tmel.Minute = tmel.Second = 0; // This was working perfectly until 3am then broke until I added this.
tmel.Month = month;
tmel.Day = day;
// year can be given as full four digit year or two digts (2010 or 10 for 2010);
//it is converted to years since 1970
if( year > 99)
tmel.Year = year - 1970;
else
tmel.Year = year + 30;
return makeTime(tmel);
}
void printdate(char const *date)
{
Serial.println((String)"cvt_date('" + date + "')");
time_t t = cvt_date(date);
Serial.println((String) month(t) + "-" + day(t) + "-" + year(t));
setTime(t);
Serial.println((String) month() + "/" + day() + "/" + year() + "\n");
}
void setup()
{
Serial.begin(9600); while (!Serial);
printdate(__DATE__); // works with the compiler macro
printdate("Jan 1 00"); // works with 2 digit years
printdate("Feb 28 01");
printdate("Mar 7 5"); // works with 1 digit years
printdate("Apr 10 1970"); // works from 1970
printdate("May 13 1980");
printdate("Jun 16 1990");
printdate("Jul 19 1997");
printdate("Aug 22 2000");
printdate("Sep 25 2010");
printdate("Oct 31 2014");
printdate("Nov 30 2020");
printdate("Dec 31 2105"); // through 2105
printdate("Dec 31 2106"); // fails at and after 2106
}
void loop(){
}
Here are the Serial Terminal results...
cvt_date('Oct 5 2014')
10-5-2014
10/5/2014
cvt_date('Jan 1 00')
1-1-2000
1/1/2000
cvt_date('Feb 28 01')
2-28-2001
2/28/2001
cvt_date('Mar 7 5')
3-7-2005
3/7/2005
cvt_date('Apr 10 1970')
4-10-1970
4/10/1970
cvt_date('May 13 1980')
5-13-1980
5/13/1980
cvt_date('Jun 16 1990')
6-16-1990
6/16/1990
cvt_date('Jul 19 1997')
7-19-1997
7/19/1997
cvt_date('Aug 22 2000')
8-22-2000
8/22/2000
cvt_date('Sep 25 2010')
9-25-2010
9/25/2010
cvt_date('Oct 31 2014')
10-31-2014
10/31/2014
cvt_date('Nov 30 2020')
11-30-2020
11/30/2020
cvt_date('Dec 31 2105')
12-31-2105
12/31/2105
cvt_date('Dec 31 2106')
11-23-1970
11/23/1970
If all you want is to use the __DATE__
and you don't need a time_t
or tmElements_t
object, the code can be much simpler.
void logname(char const *date, char *buff) {
int month, day, year;
static const char month_names[] = "JanFebMarAprMayJunJulAugSepOctNovDec";
sscanf(date, "%s %d %d", buff, &day, &year);
month = (strstr(month_names, buff)-month_names)/3+1;
sprintf(buff, "%d%02d%02d.txt", year, month, day);
}
void setup()
{
Serial.begin(9600); while (!Serial);
Serial.print("log file name: ");
char filename[16];
logname(__DATE__, filename);
Serial.println(filename);
}
void loop(){
}
Here are the Serial Terminal results...
log file name: 20141009.txt
Jerry's function looks great. Here's my attempt.. I threw in the __TIME__ as well.
#include <iostream>
#include <sstream>
using namespace std;
time_t time_when_compiled()
{
string datestr = __DATE__;
string timestr = __TIME__;
istringstream iss_date( datestr );
string str_month;
int day;
int year;
iss_date >> str_month >> day >> year;
int month;
if ( str_month == "Jan" ) month = 1;
else if( str_month == "Feb" ) month = 2;
else if( str_month == "Mar" ) month = 3;
else if( str_month == "Apr" ) month = 4;
else if( str_month == "May" ) month = 5;
else if( str_month == "Jun" ) month = 6;
else if( str_month == "Jul" ) month = 7;
else if( str_month == "Aug" ) month = 8;
else if( str_month == "Sep" ) month = 9;
else if( str_month == "Oct" ) month = 10;
else if( str_month == "Nov" ) month = 11;
else if( str_month == "Dec" ) month = 12;
else exit(-1);
for( string::size_type pos = timestr.find( ':' ); pos != string::npos; pos = timestr.find( ':', pos ) )
timestr[ pos ] = ' ';
istringstream iss_time( timestr );
int hour, min, sec;
iss_time >> hour >> min >> sec;
tm t = {0};
t.tm_mon = month-1;
t.tm_mday = day;
t.tm_year = year - 1900;
t.tm_hour = hour - 1;
t.tm_min = min;
t.tm_sec = sec;
return mktime(&t);
}
int main( int, char** )
{
cout << "Time_t when compiled: " << time_when_compiled() << endl;
cout << "Time_t now: " << time(0) << endl;
return 0;
}
Bruno's response was very useful for my Arduino Project. Here is a version with both __DATE__ and __TIME__
#include <Time.h>
#include <stdio.h>
time_t cvt_date(char const *date, char const *time)
{
char s_month[5];
int year;
tmElements_t t;
static const char month_names[] = "JanFebMarAprMayJunJulAugSepOctNovDec";
sscanf(date, "%s %hhd %d", s_month, &t.Day, &year);
sscanf(time, "%2hhd %*c %2hhd %*c %2hhd", &t.Hour, &t.Minute, &t.Second);
// Find where is s_month in month_names. Deduce month value.
t.Month = (strstr(month_names, s_month) - month_names) / 3 + 1;
// year can be given as '2010' or '10'. It is converted to years since 1970
if (year > 99) t.Year = year - 1970;
else t.Year = year + 30;
return makeTime(t);
}
void setup()
{
Serial.begin(115200);
while (!Serial);
// Show raw system strings
Serial.println(String("__DATE__ = ") + __DATE__);
Serial.println(String("__TIME__ = ") + __TIME__);
// set system time = compile time
setTime(cvt_date(__DATE__, __TIME__));
// Show actual time
Serial.println(String("System date = ") + month() + "/" + day() + "/" + year() + " " + hour() + ":" + minute() + ":" + second() + "\n");
}
void loop() {}
Here is how I modified your sample to use with mbed for Arm32 micro controllers in C++.
// Convert compile time to system time
time_t cvt_date(char const *date, char const *time)
{
char s_month[5];
int year;
struct tm t;
static const char month_names[] = "JanFebMarAprMayJunJulAugSepOctNovDec";
sscanf(date, "%s %d %d", s_month, &t.tm_mday, &year);
sscanf(time, "%2d %*c %2d %*c %2d", &t.tm_hour, &t.tm_min, &t.tm_sec);
// Find where is s_month in month_names. Deduce month value.
t.tm_mon = (strstr(month_names, s_month) - month_names) / 3 + 1;
t.tm_year = year - 1900;
return mktime(&t);
}
See: https://developer.mbed.org/users/joeata2wh/code/compile_time_to_system_time/ for complete code. Also see https://developer.mbed.org/users/joeata2wh/code/xj-Init-clock-to-compile-time-if-not-alr/ for an example of how I use it to initialize a clock chip based on the compile time.
Answer here.
Spec for the format of DATE is here.
精彩评论