转换为unix时间戳不正确

时间:2011-01-13 16:21:15

标签: c++ c unix-timestamp gmt mktime

我有一个我写的功能(如果有一个很好的标准替代品,请告诉我......)

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);
}

该函数的输入(time_str)格式为 1970-01-01 00:00:00.0 。 split_string()函数将字符串time_str拆分为包含以下内容的向量:

{1970,01,01,00,00,00}

用于填充given_time结构。

我编写了一个测试它的函数,并将其完全传递给了输入(epoch的开始)。但是,它给我的时间是21600, 1970-01-01 06:00:00 ,或 UTC + 6 。预期输出为 0 (纪元的开始)。

注意:我在美国 - 中部时区,即UTC - 6.在1970年1月1日午夜CST,时间@ UTC将是1970年1月1日06:00:00。

我的功能中是否有任何特定于我的时区的内容?我在这个函数中做错了什么,或者我可以做一些不同的事情来使它区域独立,或者至少总是UTC。

6 个答案:

答案 0 :(得分:5)

如果您使用的是glibc,则可以使用timegm函数,这是mktime的一个版本,它始终将时间解释为GMT中的时间} 时区。不幸的是,该函数的文档基本上表明它不能使用标准库调用来实现。所以,除非你拥有它,否则你有点不幸。

答案 1 :(得分:4)

mktime需要一段时间在当地时区。因此,如果您通过 1970-01-01 00:00:00当地时间,它将返回 1970-01-01 06:00:00 UTC ,因为它应该

作为替代方案,如果您使用的是glibc,则可以致电timegm。如果您没有使用glibc,则在通过弄乱TZ环境变量调用mktime时暂时将本地时间更改为UTC,如timegm联机帮助页中所述:

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;
}

此外,您对[{1}}的调用是不必要的,您可能应该设置localtime,以避免可能的夏令时问题。

答案 2 :(得分:1)

也许您应该使用gmtime代替time来摆脱时区问题。

修改 我真的不明白为什么用当前时间填充结构,然后覆盖它的所有组件。为什么不呢:

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);
}

另一个编辑:

呃,mktime也考虑当地时间。除了将您的时区区域设置为UTC之外,我不确定如何解决此问题。

答案 3 :(得分:1)

避免这些尴尬的功能并自己做数学。 POSIX指定time_t是自“the epoch”(1970-01-01 00:00:00 GMT)以来的秒数形式的算术类型,没有任何leapsecond废话(所有日子都是86400 日历秒,与 SI秒相差很小一些),所以除了一点点的误差之外,计算非常简单。

这样的日历计算是一个标准的入门编程练习,所以我相信你可以解决它或在网上找到解决方案。

顺便说一下,ISO C和POSIX省略这样一个功能的原因可能是,与涉及时区的转换不同,时区可以是任意复杂的,并且只有主机库可以在不同的应用程序中可靠且一致地执行,GMT转换是纯算术没有外部参数。

答案 4 :(得分:0)

当您调用mktime时,它会将参数解释为本地时间。 你还使用了像“本地时间”这样似乎毫无用处的功能,我想你可以放弃它们。

答案 5 :(得分:0)

您可以在strptime周围编写一个包装来进行解析。

struct tm given_time;

strptime(time_str.c_str(), "%Y-%m-%d %H:%M:%S", &given_time);

return mktime(&given_time);
@Josh Kelley的回答彻底解释了时区问题。