Как конвертировать дату в формате UTC & amp; время time_t в C ++?

Я хочу конвертировать дату в формате UTC & время, указанное в числах для года, месяца, дня и т. д. time_t. Некоторые системы предлагают такие функции, как mkgmtime или же timegm для этого, но это не является стандартным и не существует в моей системе Solaris.

Единственное решение, которое я нашел до сих пор, заключается в установке местного часового пояса в UTC с помощью setenv, а затем mktime, Однако этот подход не является потокобезопасным, медленным, не переносимым и даже вызывает утечку памяти в моей системе.

Я также видел подходы, которые пытались определить текущее смещение UTC, используя gmtime а затем добавить это к результату mktime, Но, насколько я видел, все эти подходы имели пробелы. Ведь перевод с местного времени на UTC не уникален.

Как вы думаете, это лучшее решение?

7

Решение

Я решил реализовать свою собственную версию mkgmtime, и это оказалось проще, чем я думал.

const int SecondsPerMinute = 60;
const int SecondsPerHour = 3600;
const int SecondsPerDay = 86400;
const int DaysOfMonth[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};

bool IsLeapYear(short year)
{
if (year % 4 != 0) return false;
if (year % 100 != 0) return true;
return (year % 400) == 0;
}

time_t mkgmtime(short year, short month, short day, short hour, short minute, short second)
{
time_t secs = 0;
for (short y = 1970; y < year; ++y)
secs += (IsLeapYear(y)? 366: 365) * SecondsPerDay;
for (short m = 1; m < month; ++m) {
secs += DaysOfMonth[m - 1] * SecondsPerDay;
if (m == 2 && IsLeapYear(year)) secs += SecondsPerDay;
}
secs += (day - 1) * SecondsPerDay;
secs += hour * SecondsPerHour;
secs += minute * SecondsPerMinute;
secs += second;
return secs;
}

Моей главной заботой было то, что mkgmtime должен соответствовать gmtime, Такой, что gmtime(mktime(t)) возвращает исходные входные значения. Поэтому я сравнил результаты для всех кратных 61 от 0 до MAX_INT для time_t, и они действительно равны (по крайней мере, в моей системе). Поэтому приведенная выше процедура является правильной.

Этот результат также означает, что библиотека C не учитывает дополнительные секунды, что само по себе плохо, но хорошо для моей цели. Эти две функции будут оставаться согласованными в течение длительного времени. Чтобы быть абсолютно уверенным, мой класс Timestamp, который использует эту функцию, всегда выполняет быструю проверку запуска программы и подтверждает согласованность для пары значащих значений.

9

Другие решения

Для полноты, вот версия mkgmtime (), которая принимает struct tm * в качестве аргумента:

static time_t mkgmtime(const struct tm *ptm) {
time_t secs = 0;
// tm_year is years since 1900
int year = ptm->tm_year + 1900;
for (int y = 1970; y < year; ++y) {
secs += (IsLeapYear(y)? 366: 365) * SecondsPerDay;
}
// tm_mon is month from 0..11
for (int m = 0; m < ptm->tm_mon; ++m) {
secs += DaysOfMonth[m] * SecondsPerDay;
if (m == 1 && IsLeapYear(year)) secs += SecondsPerDay;
}
secs += (ptm->tm_mday - 1) * SecondsPerDay;
secs += ptm->tm_hour       * SecondsPerHour;
secs += ptm->tm_min        * SecondsPerMinute;
secs += ptm->tm_sec;
return secs;
}
9

Как отмечено выше, в то время как time_t обычно представляет секунды, прошедшие с 1 января 1970 года, это нигде не указано. Реализация, которая использует другое внутреннее представление, может появиться в любое время, и любой код, который делает предположения о внутренней работе time_t там не будет работать правильно.

Подумав, я придумал следующее:

time_t mkgmtime(struct tm * pt) {
time_t ret;

/* GMT and local time */
struct tm * pgt, * plt;

ret = mktime(pt);

pgt = g_memdup(gmtime(ret), sizeof(struct tm));
plt = g_memdup(localtime(ret), sizeof(struct tm));

plt->tm_year -= pgt->tm_year - plt->tm_year;
plt->tm_mon -= pgt->tm_mon - plt->tm_mon;
plt->tm_mday -= pgt->tm_mday - plt->tm_mday;
plt->tm_hour -= pgt->tm_hour - plt->tm_hour;
plt->tm_min -= pgt->tm_min - plt->tm_min;
plt->tm_sec -= pgt->tm_sec - plt->tm_sec;

ret = mktime(plt);

g_free(pgt);
g_free(plt);

return ret;
}

Можно было бы оптимизировать это дальше, опустив plt (с помощью pt на своем месте и опуская localtime() а также g_free(plt) звонки).

Это должно работать во всех реализациях, которые выставляют mktime(), gmtime() а также localtime()включая даты перехода на летнее время. (mktime() будет «нормализовать» значения вне диапазона, например, превращение 35 января в 4 февраля; Я также ожидал бы, что 9:50 летнего времени в середине зимы станут 8:50 стандартного времени.)

Он страдает от одной потенциальной ошибки: если смещение UTC часового пояса изменяется по причинам, не отраженным во флаге DST, временные метки вокруг времени переключения могут интерпретироваться неправильно: стандартный случай — когда законодательство меняет свой часовой пояс (например, Литва изменилась с Советское время для CET после независимости, и для EET через несколько лет). В некоторых законодательных актах в середине лета было установлено двойное летнее время, с циклическим изменением 3 различных смещений UTC в год, которое флаг DST не может представлять.

0
По вопросам рекламы [email protected]