2016-12-01 10 views
4

У меня есть программа, которая была изначально написанной для Linux, но теперь у меня есть требование, чтобы он работает на Solaris 10.Альтернативы timegm на Solaris

Часть этой программы использует функцию timegm для преобразования struct tm в a time_t эпоха секунд значение. Время ввода ссылается на UTC.

Попытка скомпилировать эту программу на Solaris, она не работает, потому что timegm не может быть найден. После некоторого googling я понял, что эта функция была удалена из Solaris давным-давно (и даже личная страница Linux рекомендует не использовать ее, потому что она не стандартизирована).

Однако я до сих пор не смог найти альтернативную функцию, которая принимает struct tm, ссылаясь на UTC и преобразует в эпоху. Большинство ссылок, которые я нашел в сети, рекомендую использовать mktime, однако эта функция интерпретирует входы со ссылкой на местный часовой пояс системы.

Обратите внимание, что я не хочу использовать tzset, чтобы установить часовой пояс в UTC, так как это будет иметь другие побочные эффекты для программы.

Так что мой вопрос: как я могу преобразовать значение времени разбивки struct tm, выраженное относительно UTC, в эпоху времени, в отсутствие timegm?

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

ответ

5

Вы можете использовать days_from_civil который described here in detail

// Returns number of days since civil 1970-01-01. Negative values indicate 
// days prior to 1970-01-01. 
// Preconditions: y-m-d represents a date in the civil (Gregorian) calendar 
//     m is in [1, 12] 
//     d is in [1, last_day_of_month(y, m)] 
//     y is "approximately" in 
//     [numeric_limits<Int>::min()/366, numeric_limits<Int>::max()/366] 
//     Exact range of validity is: 
//     [civil_from_days(numeric_limits<Int>::min()), 
//     civil_from_days(numeric_limits<Int>::max()-719468)] 
template <class Int> 
constexpr 
Int 
days_from_civil(Int y, unsigned m, unsigned d) noexcept 
{ 
    static_assert(std::numeric_limits<unsigned>::digits >= 18, 
      "This algorithm has not been ported to a 16 bit unsigned integer"); 
    static_assert(std::numeric_limits<Int>::digits >= 20, 
      "This algorithm has not been ported to a 16 bit signed integer"); 
    y -= m <= 2; 
    const Int era = (y >= 0 ? y : y-399)/400; 
    const unsigned yoe = static_cast<unsigned>(y - era * 400);  // [0, 399] 
    const unsigned doy = (153*(m + (m > 2 ? -3 : 9)) + 2)/5 + d-1; // [0, 365] 
    const unsigned doe = yoe * 365 + yoe/4 - yoe/100 + doy;   // [0, 146096] 
    return era * 146097 + static_cast<Int>(doe) - 719468; 
} 

для преобразования {год, месяц, день} тройка в tm отсчету дней с начала эпохи (1970-01-01). Будьте осторожны при преобразовании этих полей из tm для их эксцентриситетов (например, tm_year + 1900).

Умножьте это количество дней на 86400 и добавьте к ним данные {часы, минуты, секунды} из tm (каждая конвертируется в секунды).

И все готово. Не беспокойтесь о прыжках секунд, timegm тоже не беспокоился о них. Если вы действительно обеспокоены прыжковыми секундами, у меня есть C++11/14 solution available to deal with that, но я предполагаю, что это больше, чем вы хотите попасть.

Не отключайте синтаксис C++ 14, показанный выше. Тривиально преобразовать этот алгоритм в C (или любой другой язык, если на то пошло).

+2

NIce один. Эта страница алгоритмов вашей даты - фантастический ресурс – harmic

0

Per the POSIX standard for tzset():

СИНТАКСИСА

#include <time.h> 

extern int daylight; 
extern long timezone; 

extern char *tzname[2]; 
void tzset(void); 

...

tzset() функция также устанавливает внешний переменный дневной свет 0, если преобразование летнего времени никогда не должно применяется для используемого часового пояса; иначе, отличное от нуля. Внешняя переменная timezone должна быть установлена ​​в разницу в секундах между Согласованное универсальное время (UTC) и местное стандартное время.

Вы должны быть в состоянии назвать tzset(), чтобы установить значение в timezone, а затем использовать mktime(), чтобы получить время в текущем часовом поясе, а затем применить разницу в переменной timezone к результату от mktime() преобразовать что результат UTC.

У меня нет доступа к Solaris прямо сейчас, чтобы проверить это.

+0

Спасибо. Я думал об этом, но разница между временем по Гринвичу и местным временем не постоянна, это зависит от времени преобразования. Например, если я конвертирую «2016-12-01 10:00» в свой часовой пояс в UTC, разница будет отличаться от «2016-06-01 10:00» из-за летнего сбережения. – harmic