Неверное преобразование метки времени в Unix - PullRequest
3 голосов
/ 13 января 2011

У меня есть функция, которую я написал (если есть хорошая стандартная замена, пожалуйста, дайте мне знать ...)

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: 00 или UTC + 6 .Ожидаемый результат - 0 (начало эпохи).

Примечание: я нахожусь в часовом поясе США и Центральном регионе США, который является UTC - 6. В полночь 1 января 1970 года по Гринвичу время @ UTC будет 1 января 1970 года в 06:00:00.

Есть ли в моей функции что-то, что делает ее специфичной для моего часового пояса?Я делаю что-то не так в этой функции, или я могу сделать что-то другое, чтобы сделать ее независимой от зоны, или, по крайней мере, всегда UTC.

Ответы [ 6 ]

5 голосов
/ 13 января 2011

Если вы используете glibc , у вас есть функция timegm, которая является версией mktime, которая всегда интерпретирует время, как если бы оно было в часовом поясе GMT.К сожалению, документация для этой функции в основном утверждает, что она не может быть иначе реализована с использованием стандартных библиотечных вызовов.Так что вам не повезло, если у вас нет этого.

4 голосов
/ 13 января 2011

mktime занимает время в местном часовом поясе.Так, если вы передадите его 1970-01-01 00:00:00 по местному времени , он вернет 1970-01-01 06:00:00 UTC , как и должно быть.

В качестве альтернативы вы можете позвонить timegm , если вы используете glibc.Если вы не используете glibc, вы временно меняете местное время на UTC при вызове mktime, связываясь с переменной окружения TZ, как описано на man-странице 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, чтобы избежать возможных проблем с переходом на летнее время.

1 голос
/ 13 января 2011

Просто избегайте этих неловких функций и делайте математику самостоятельно.POSIX указывает, что time_t является арифметическим типом в форме секунд, прошедших с начала эпохи (1970-01-01 00:00:00 по Гринвичу) без всякой бессмысленной ерунды (все дни ровно 86400 календарные секунды , которые отличаются от SI секунд незначительным количеством), поэтому, помимо небольшой логики в високосный год, вычисления чрезвычайно просты.

Подобные вычисления в календаре являются стандартным вводным упражнением по программированию, поэтомуЯ уверен, что вы можете разобраться с этим или найти решения в Интернете.

Кроме того, возможно, причина в том, что ISO C и POSIX опускают такую ​​функцию, состоит в том, что, в отличие от преобразований с использованием часовых поясов, которые могут быть сколь угодно сложными икоторый только библиотека хоста может надежно и согласованно выполнять в разных приложениях, преобразования по Гринвичу являются чистой арифметикой без внешних параметров.

1 голос
/ 13 января 2011

Возможно, вам следует использовать gmtime вместо time, чтобы избавиться от проблем с часовым поясом.

Edit: Я не очень понимаю, почему вы заполняете структуру текущим временем, а затем перезаписываете все ее компоненты. Почему бы просто:

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.

0 голосов
/ 13 января 2011

Вы можете написать обертку вокруг strptime, чтобы выполнить синтаксический анализ.

struct tm given_time;

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

return mktime(&given_time);

@ Ответ Джоша Келли подробно объясняет проблему часовых поясов.

0 голосов
/ 13 января 2011

Когда вы вызываете mktime, он интерпретирует параметр как местное время.Вы также использовали функцию, подобную «localtime», которая кажется бесполезной, и я думаю, что вы можете удалить их.

...