Conversion to unix timestamp incorrect

2019-07-04 00:11发布

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.

6条回答
叼着烟拽天下
2楼-- · 2019-07-04 00:53

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.

查看更多
唯我独甜
3楼-- · 2019-07-04 00:54

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.

查看更多
Evening l夕情丶
4楼-- · 2019-07-04 01:01

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.

查看更多
Luminary・发光体
5楼-- · 2019-07-04 01:07

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.

查看更多
唯我独甜
6楼-- · 2019-07-04 01:13

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.

查看更多
成全新的幸福
7楼-- · 2019-07-04 01:13

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.

查看更多
登录 后发表回答