3

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

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 结构。

我写了一个函数来测试它,并准确地传递了那个输入(纪元的开始)。但是,它给我的时间是 21600,即1970-01-01 06:00:00UTC+6。预期输出为0(纪元开始)。

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

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

4

6 回答 6

5

如果您使用的是glibc ,则可以使用该timegm功能,该功能mktime始终将时间解释为好像在GMT时区中一样。不幸的是,该函数的文档基本上表明它不能使用标准库调用来实现。因此,除非您拥有它,否则您有点不走运。

于 2011-01-13T16:38:08.443 回答
4

mktime在当地时区需要时间。所以,如果你传递它1970-01-01 00:00:00 local time,它应该返回1970-01-01 06:00:00 UTC

作为替代方案,如果您使用的是 glibc ,您可以调用timegm 。如果您不使用 glibc,则在调用 mktime 时通过弄乱 TZ 环境变量将本地时间临时更改为 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;
}

此外,您的呼叫localtime是不必要的,您可能应该设置given_time->tm_isdst,以避免可能的夏令时问题。

于 2011-01-13T16:41:19.113 回答
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 之外,我不太确定如何解决这个问题。

于 2011-01-13T16:29:36.700 回答
1

只要避免这些尴尬的功能,自己做数学。POSIX 指定它time_t是自“纪元”(格林威治标准时间 1970 年 1 月 1 日 00:00:00)以来以秒为形式的算术类型,没有任何闰秒废话(所有天都是 86400日历秒,与SI 秒相差一个少量),所以除了一点闰年逻辑外,计算非常简单。

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

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

于 2011-01-13T19:02:54.420 回答
0

当您调用 mktime 时,它​​会将参数解释为本地时间。您还使用了“localtime”之类的似乎没用的功能,我认为您可以放弃它们。

于 2011-01-13T16:37:44.950 回答
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 的回答彻底解释了时区问题。

于 2011-01-13T16:43:02.013 回答