timegm跨平台

时间:2013-05-20 10:46:24

标签: c++ visual-c++ msvcrt crt

我正在使用Visual Studio c ++ Compiler(2010),但该库具有不同的ANSI C和POSIX库函数的实现。

ANSI C函数和Windows CRT实现之间有什么区别?例如,tzset()_tzset()setenv() ans _setenv()之间有什么区别?似乎以同样的方式做同样的事情......

我正在使用msvc(2010),我是否更喜欢Windows CRT实现?

编辑1

嗯,我希望以可移植的方式转换在time_t中用UTC表示的struct tm,但是没有可移植的方法来做到这一点。我要为不同的平台(Android,Linux,Windows,Windows CE)编写函数。

我见过this stackoverflow post使用setenvgetenvtzset

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;
}

5 个答案:

答案 0 :(得分:19)

我在Windows上使用以下宏:

#define timegm _mkgmtime

_mkgmtime也是如此。

答案 1 :(得分:7)

当David Cutler的团队在1989年开始使用Windows NT设计时,他们还不知道哪个api会占据主导地位。所以他们创建了三个。 Win32是对16位版本的Windows API的改编。支持OS / 2,该操作系统应该取代DOS但不支持。 Posix是第三个,因为美国政府当时指出他们只会考虑使用遵循新兴Posix标准的操作系统。

你提到的tzset()函数是Posix api的遗留物。你可能拼错了putenv(),同样的故事。子系统运行得不好,Win32在很大程度上赢得了api之战,并且在2001年从Windows中删除了Posix支持。微软保留了对Posix功能的支持,但是由于它们不是标准的一部分,所以用一个领先的下划线重命名它们C库。当您使用函数的非前缀版本时,您应该获得弃用警告。听起来像#defined _CRT_NONSTDC_NO_DEPRECATE来压制它们。最好不要那样做。支持标准的C库函数。

答案 2 :(得分:3)

对于大多数功能,我知道,没有区别。

名称中的下划线强调这些是标准C函数:AFAIK,ANSI C中没有tzsetsetenv函数。它们是主要是由MS CRT实现的POSIX功能,作为其他操作系统的可移植性的辅助。

但他们没有声称POSIX兼容性,这就是下划线的原因。这就是为什么你应该小心并阅读有关这些功能的MS文档...那里有恶魔!

答案 3 :(得分:2)

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;
}

应该没事。

答案 4 :(得分:2)

// 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中的tm转换为time_t的可移植方式。

请注意,它不会修改/标准化tm结构,也不会更改任何tz设置。