Ответ 1
Я использую следующий макрос в Windows:
#define timegm _mkgmtime
как _mkgmtime делает то же самое.
Я использую Visual Studio c++ Compiler (2010), но библиотека имеет различную реализацию функций ANSI C и POSIX.
В чем разница между функцией ANSI C и реализацией Windows CRT? Например, в чем разница между tzset()
и _tzset()
или setenv()
ans _setenv()
? Кажется, что то же самое происходит одинаково...
Я использую msvc (2010), должен ли я предпочесть реализацию Windows CRT?
ИЗМЕНИТЬ 1
Ну, я хочу преобразовать в переносном стиле struct tm, выраженный в UTC в time_t
, но нет никакого портативного способа сделать это. Я должен написать функцию для разных платформ (Android, Linux, Windows, Windows CE).
Я видел qaru.site/info/280378/... которая использует 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;
}
Я использую следующий макрос в Windows:
#define timegm _mkgmtime
как _mkgmtime делает то же самое.
Когда команда Дэвида Катлера начала работать над дизайном Windows NT, еще в 1989 году они еще не знали, какой апи будет доминировать. Таким образом, они создали три из них. Win32 была адаптирована к 16-битной версии Windows api. OS/2 была поддержана, операционная система, которая должна была заменить DOS, но не сделала этого. И Posix был третьим, добавил, потому что тогда правительство США уточнило, что они будут рассматривать только операционные системы, которые следуют за новым стандартом Posix.
Функция tzset(), о которой вы говорите, является слева от Posix api. Вы, вероятно, ошибочно написали putenv(), ту же историю. Подсистема не увенчалась успехом, Win32 выиграла битку api в большой степени, а поддержка Posix была удалена из Windows в 2001 году. Microsoft поддерживала поддержку функций Posix, но переименовала их с лидирующим подчеркиванием, поскольку они не являются частью стандарта C библиотеки. Вы должны получать предупреждения об устаревании, когда используете неиспользуемую версию функций. Похоже, вы #defined _CRT_NONSTDC_NO_DEPRECATE, чтобы подавить их. Лучше не делать этого. Поддерживайте стандартные функции библиотеки C.
Для большинства функций, о которых я знаю, нет никакой разницы.
Подчеркивание имен должно подчеркнуть, что они не являются стандартными функциями C: AFAIK, в ANSI C нет функций tzset
или setenv
. Они в основном являются функциями 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, <ime);
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;
}
Все должно быть в порядке.
// 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;
}
Это переносимый способ преобразования тм в UTC в time_t.
Обратите внимание, что он не изменяет/не нормализует структуру tm и не меняет никаких настроек tz.