将日期和时间数字转换为time_t并指定时区

bgu*_*uiz 8 c++ timezone datetime time-t

我有以下整数:

int y, mon, d, h, min, s;
Run Code Online (Sandbox Code Playgroud)

他们的价值观是:2012,06,27,12,47,53分别.我想代表"2012/06/27 12:47:53 UTC"的日期时间,如果我在我的应用程序中的其他地方选择了"UTC",或者"2012/06/27 12:47:53 AEST"如果我在我的应用程序中的其他地方选择了"AEST".

我想将其转换为a time_t,这是我目前使用的代码:

struct tm timeinfo;
timeinfo.tm_year = year - 1900;
timeinfo.tm_mon = mon - 1;
timeinfo.tm_mday = day;
timeinfo.tm_hour = hour;
timeinfo.tm_min = min;
timeinfo.tm_sec = sec;
//timeinfo.tm_isdst = 0; //TODO should this be set?

//TODO find POSIX or C standard way to do covert tm to time_t without in UTC instead of local time
#ifdef UNIX
return timegm(&timeinfo);
#else
return mktime(&timeinfo); //FIXME Still incorrect
#endif
Run Code Online (Sandbox Code Playgroud)

所以,我使用的是tm structmktime,但是这都不尽如人意,因为它总是假设我的本地时区.

这样做的正确方法是什么?

以下是我到目前为止提出的解决方案.它主要做三件事之一:

  1. 如果是UNIX,只需使用 timegm
  2. 如果不是UNIX
    1. 或者,使用UTC纪元和本地纪元之间的差异做数学作为偏移
      • 预订:数学可能不正确
    2. 或者,暂时将"TZ"环境变量设置为UTC
      • 预订:如果/此代码需要多线程,则会跳闸
namespace tmUtil
{
    int const tm_yearCorrection = -1900;
    int const tm_monthCorrection = -1;
    int const tm_isdst_dontKnow = -1;

#if !defined(DEBUG_DATETIME_TIMEGM_ENVVARTZ) && !(defined(UNIX) && !defined(DEBUG_DATETIME_TIMEGM))
    static bool isLeap(int year)
    {
        return
            (year % 4) ? false
            : (year % 100) ? true
            : (year % 400) ? false
            : true;
    }

    static int daysIn(int year)
    {
        return isLeap(year) ? 366 : 365;
    }
#endif
}

time_t utc(int year, int mon, int day, int hour, int min, int sec)
{
    struct tm time = {0};
    time.tm_year = year + tmUtil::tm_yearCorrection;
    time.tm_mon = mon + tmUtil::tm_monthCorrection;
    time.tm_mday = day;
    time.tm_hour = hour;
    time.tm_min = min;
    time.tm_sec = sec;
    time.tm_isdst = tmUtil::tm_isdst_dontKnow;

    #if defined(UNIX) && !defined(DEBUG_DATETIME_TIMEGM) //TODO remove && 00
        time_t result;
        result = timegm(&time);
        return result;
    #else
        #if !defined(DEBUG_DATETIME_TIMEGM_ENVVARTZ)
            //TODO check that math is correct
            time_t fromEpochUtc = mktime(&time);

            struct tm localData;
            struct tm utcData;
            struct tm* loc = localtime_r (&fromEpochUtc, &localData);
            struct tm* utc = gmtime_r (&fromEpochUtc, &utcData);
            int utcYear = utc->tm_year - tmUtil::tm_yearCorrection;
            int gmtOff =
                (loc-> tm_sec - utc-> tm_sec)
                + (loc-> tm_min - utc-> tm_min) * 60
                + (loc->tm_hour - utc->tm_hour) * 60 * 60
                + (loc->tm_yday - utc->tm_yday) * 60 * 60 * 24
                + (loc->tm_year - utc->tm_year) * 60 * 60 * 24 * tmUtil::daysIn(utcYear);

            #ifdef UNIX
                if (loc->tm_gmtoff != gmtOff)
                {
                    StringBuilder err("loc->tm_gmtoff=", StringBuilder((int)(loc->tm_gmtoff)), " but gmtOff=", StringBuilder(gmtOff));
                    THROWEXCEPTION(err);
                }
            #endif

            int resultInt = fromEpochUtc + gmtOff;
            time_t result;
            result = (time_t)resultInt;
            return result;
        #else
            //TODO Find a way to do this without manipulating environment variables
            time_t result;
            char *tz;
            tz = getenv("TZ");
            setenv("TZ", "", 1);
            tzset();
            result = mktime(&time);
            if (tz)
                setenv("TZ", tz, 1);
            else
                unsetenv("TZ");
            tzset();
            return result;
        #endif
    #endif
}
Run Code Online (Sandbox Code Playgroud)

NB StringBuilder是一个内部类,对于这个问题的目的并不重要.

更多信息:

我知道使用boost等可以轻松完成.但这不是和选择.我需要它以数学方式完成,或使用ac或c ++标准函数或其组合.

timegm似乎解决了这个问题,然而,它似乎不是C/POSIX标准的一部分.此代码目前在多个平台(Linux,OSX,WIndows,iOS,Android(NDK))上编译,因此我需要找到一种方法使其适用于所有这些平台,即使解决方案涉及#ifdef $PLATFORM类型的事情.

smo*_*ing 4

这让我有点想吐在嘴里,但你可以将其转换为字符串strftime(),替换字符串中的时区,然后将其转换回withstrptime()和。详细地:time_tmktime()

#ifdef UGLY_HACK_VOIDS_WARRANTY
time_t convert_time(const struct tm* tm)
{
    const size_t BUF_SIZE=256;
    char buffer[BUF_SIZE];
    strftime(buffer,256,"%F %H:%M:%S %z", tm);
    strncpy(&buffer[20], "+0001", 5); // +0001 is the time-zone offset from UTC in hours
    struct tm newtime = {0};
    strptime(buffer, "%F %H:%M:%S %z", &newtime);
    return mktime(&newtime);
}
#endif
Run Code Online (Sandbox Code Playgroud)

然而,我强烈建议你说服权力,毕竟提升是一种选择。Boost 对自定义时区有很好的支持。还有其他库也可以优雅地做到这一点。