问题
I have a function that I wrote (if there is a good standard substitute, please let me know...)
time_t get_unix_time(string time_str) {
time_t loctime;
time(&loctime);
struct tm *given_time;
time_str = time_str.substr(0, time_str.find_first_of('.'));
replace(time_str.begin(), time_str.end(), ':', ',');
replace(time_str.begin(), time_str.end(), '-', ',');
replace(time_str.begin(), time_str.end(), '/', ',');
replace(time_str.begin(), time_str.end(), ' ', ',');
given_time = localtime(&loctime);
vector<string> trecord = split_string(time_str, ',');
given_time->tm_year = atoi(trecord.at(0).c_str()) - 1900;
given_time->tm_mon = atoi(trecord.at(1).c_str()) - 1;
given_time->tm_mday = atoi(trecord.at(2).c_str());
given_time->tm_hour = atoi(trecord.at(3).c_str());
given_time->tm_min = atoi(trecord.at(4).c_str());
given_time->tm_sec = atoi(trecord.at(5).c_str());
return mktime(given_time);
}
The input (time_str) to the function is of the format 1970-01-01 00:00:00.0. The split_string() function splits the string time_str
into a vector containing:
{ 1970, 01, 01, 00, 00, 00 }
which is used to fill in the given_time structure.
I wrote a function to test it, and passed it exactly that input (start of epoch). However, the time it gives me back is 21600, which is 1970-01-01 06:00:00, or UTC+6. The expected output is 0 (start of the epoch).
Note: that I am in the US-Central time zone, which is UTC - 6. At midnight on 1st Jan 1970 CST, time @ UTC would be 1st Jan 1970 06:00:00.
Is there anything in my function that is making it specific to my timezone? Am I doing something wrong in this function, or can I do something different to make it zone independent, or at least always UTC.
回答1:
If you are using glibc you have the timegm
function at your disposal, which is a version of mktime
that always interprets the time as if it were in the GMT
timezone. Unfortunately, the documentation for that function basically states that it cannot otherwise be implemented using standard library calls. So you're sort of out of luck unless you have it.
回答2:
mktime
takes a time in the local time zone. So, if you pass it 1970-01-01 00:00:00 local time, it returns 1970-01-01 06:00:00 UTC, as it should.
As an alternative, you can call timegm, if you're using glibc. If you're not using glibc, you temporarily change the local time to UTC when calling mktime by messing with the TZ environment variable, as described on the timegm manpage:
time_t my_timegm (struct tm *tm) {
time_t ret;
char *tz;
tz = getenv("TZ");
setenv("TZ", "", 1);
tzset();
ret = mktime(tm);
if (tz)
setenv("TZ", tz, 1);
else
unsetenv("TZ");
tzset();
return ret;
}
Also, your call to localtime
is unnecessary, and you probably ought to set given_time->tm_isdst
, to avoid possible daylight savings time issues.
回答3:
Maybe you should use gmtime
instead of time
, to rid yourself of timezone issues.
Edit: I don't really understand why you fill the structure with the current time, then overwrite all its components. Why not just:
time_t get_unix_time(const string& time_str)
{
vector<string> trecord = split_string(time_str, ',');
tm given_time;
given_time.tm_year = atoi(trecord.at(0).c_str()) - 1900;
given_time.tm_mon = atoi(trecord.at(1).c_str()) - 1;
given_time.tm_mday = atoi(trecord.at(2).c_str());
given_time.tm_hour = atoi(trecord.at(3).c_str());
given_time.tm_min = atoi(trecord.at(4).c_str());
given_time.tm_sec = atoi(trecord.at(5).c_str());
return mktime(&given_time);
}
Another edit:
Ugh, mktime
considers local time too. I'm not really sure how you can get around this other than setting your timezone locale to UTC.
回答4:
Just avoid these awkward functions and do the math yourself. POSIX specifies that time_t
is an arithmetic type in the form of seconds since "the epoch" (1970-01-01 00:00:00 GMT) without any leapsecond nonsense (all days are exactly 86400 calendar seconds, which differ from SI seconds by a tiny amount), so aside from a little leapyear logic, the computation is extremely straightforward.
Calendar calculations like this are a standard introductory programming exercise so I'm sure you can work it out or find solutions on the web.
As an aside, perhaps the reason ISO C and POSIX omit such a function is that, unlike conversions involving timezones which can be arbitrarily complex and which only the host's library can perform reliably and consistently across different applications, GMT conversions are pure arithmetic with no external parameters.
回答5:
When you call mktime, it interpretes the parameter as a local time. You have also used function like "localtime" that seems to be useless and I think you can drop them.
回答6:
You could write a wrapper around strptime
to do the parsing.
struct tm given_time;
strptime(time_str.c_str(), "%Y-%m-%d %H:%M:%S", &given_time);
return mktime(&given_time);
@Josh Kelley's answer explains the timezone issue thoroughly.
来源:https://stackoverflow.com/questions/4682350/conversion-to-unix-timestamp-incorrect