Timegm кросс-платформенный

Я использую Visual Studio C++ Compiler ( 2010), но библиотека имеет различную реализацию функций библиотек ANSI C и POSIX.

В чем разница между функцией ANSI C и реализацией Windows CRT? Например, в чем разница между tzset() а также _tzset() или же setenv() анс _setenv()? Кажется, делают то же самое таким же образом...

Я использую msvc ( 2010), я предпочитаю реализацию Windows CRT?

РЕДАКТИРОВАТЬ 1

Ну, я хочу, чтобы преобразовать переносимым способом структуры тм, выраженные в UTC в time_t, но нет портативного способа сделать это. Я должен написать функцию для другой платформы (Android, Linux, Windows, Windows CE).

Я видел этот пост, который использует setenv, getenv а также tzset

Edit2

К сожалению, после некоторого теста я обнаружил, что getenv("TZ") возвращает нулевой указатель на окна. Но почему так сложно преобразовать структуру времени UTC в time_t?

Редактировать 3

Из Boost я обнаружил этот фрагмент кода в boost/chrono/io/time_point_io.hpp. Надеюсь, это поможет мне.

inline int32_t is_leap(int32_t year)
{
  if(year % 400 == 0)
  return 1;
  if(year % 100 == 0)
  return 0;
  if(year % 4 == 0)
  return 1;
  return 0;
}
inline int32_t days_from_0(int32_t year)
{
  year--;
  return 365 * year + (year / 400) - (year/100) + (year / 4);
}
inline int32_t days_from_1970(int32_t year)
{
  static const int days_from_0_to_1970 = days_from_0(1970);
  return days_from_0(year) - days_from_0_to_1970;
}
inline int32_t days_from_1jan(int32_t year,int32_t month,int32_t day)
{
  static const int32_t days[2][12] =
  {
    { 0,31,59,90,120,151,181,212,243,273,304,334},
    { 0,31,60,91,121,152,182,213,244,274,305,335}
  };
  return days[is_leap(year)][month-1] + day - 1;
}

inline time_t internal_timegm(std::tm const *t)
{
  int year = t->tm_year + 1900;
  int month = t->tm_mon;
  if(month > 11)
  {
    year += month/12;
    month %= 12;
  }
  else if(month < 0)
  {
    int years_diff = (-month + 11)/12;
    year -= years_diff;
    month+=12 * years_diff;
  }
  month++;
  int day = t->tm_mday;
  int day_of_year = days_from_1jan(year,month,day);
  int days_since_epoch = days_from_1970(year) + day_of_year;

  time_t seconds_in_day = 3600 * 24;
  time_t result = seconds_in_day * days_since_epoch + 3600 * t->tm_hour + 60 * t->tm_min + t->tm_sec;

  return result;
}

4 ответа

Решение

Я использую следующий макрос в Windows:

#define timegm _mkgmtime

как _mkgmtime делает то же самое.

// Algorithm: http://howardhinnant.github.io/date_algorithms.html
int days_from_civil(int y, int m, int d)
{
    y -= m <= 2;
    int era = y / 400;
    int yoe = y - era * 400;                                   // [0, 399]
    int doy = (153 * (m + (m > 2 ? -3 : 9)) + 2) / 5 + d - 1;  // [0, 365]
    int doe = yoe * 365 + yoe / 4 - yoe / 100 + doy;           // [0, 146096]
    return era * 146097 + doe - 719468;
}

time_t timegm(tm const* t)     // It  does not modify broken-down time
{
    int year = t->tm_year + 1900;
    int month = t->tm_mon;          // 0-11
    if (month > 11)
    {
        year += month / 12;
        month %= 12;
    }
    else if (month < 0)
    {
        int years_diff = (11 - month) / 12;
        year -= years_diff;
        month += 12 * years_diff;
    }
    int days_since_1970 = days_from_civil(year, month + 1, t->tm_mday);

    return 60 * (60 * (24L * days_since_1970 + t->tm_hour) + t->tm_min) + t->tm_sec;
}

Это переносимый способ конвертировать tm в формате UTC в time_t.

Обратите внимание, что он не изменяет / нормализует структуру tm и не изменяет никаких настроек tz.

Когда команда Дэвида Катлера начала разработку Windows NT в 1989 году, они еще не знали, какой API будет доминирующим. Таким образом, они создали три из них. Win32 была адаптацией 16-битной версии Windows API. OS/2 была поддержана, операционная система, которая должна была заменить DOS, но не сделала этого. И Posix был третьим, добавленным, потому что правительство США тогда указало, что они рассмотрят только использование операционных систем, которые следуют за появляющимся стандартом Posix.

Упомянутая вами функция tzset() является остатком API Posix. Вы наверное неправильно написали putenv(), та же история. Подсистема не работала хорошо, Win32 выиграл битву api, а поддержка Posix была удалена из Windows в 2001 году. Microsoft сохранила поддержку функций Posix, но переименовала их с подчеркиванием, так как они не являются частью стандарта. С библиотека. Вы должны получать предупреждения об устаревании, когда используете версию без префикса функций. Похоже, вы #defined _CRT_NONSTDC_NO_DEPRECATE, чтобы подавить их. Лучше не делать этого. Воспользуйтесь стандартными функциями библиотеки C.

Для большинства функций, о которых я знаю, нет никакой разницы.

Подчеркивание в именах подчеркивает, что это не стандартные функции C: AFAIK, нет tzset ни setenv функции в ANSI C. В основном это функции POSIX, которые реализованы MS CRT в качестве вспомогательного средства для переносимости из других операционных систем.

Но они не претендуют на совместимость с POSIX, поэтому подчеркиваем. И именно поэтому вы должны быть осторожны и читать документацию MS об этих функциях... там есть демоны!

Моя реализация timegm работает на окнах.

time_t timegm(struct tm * a_tm)
{
    time_t ltime = mktime(a_tm);
    struct tm tm_val;
    gmtime_s(&tm_val, &ltime);
    int offset = (tm_val.tm_hour - a_tm->tm_hour);
    if (offset > 12)
    {
        offset = 24 - offset;
    }
    time_t utc = mktime(a_tm) - offset * 3600;
    return utc;
}

Все должно быть в порядке.

Другие вопросы по тегам