转换日期和时间号码的time_t,并指定时区(Convert date and time numbe

2019-07-30 03:35发布

我有以下的整数:

int y, mon, d, h, min, s;

它们的值是: 20120627124753分别。 我要代表“2012/06/27 12时47分53秒UTC”的日期时间,如果我有,如果我选择在我的应用“UTC”别的地方,或者“2012/06/27 12时47分53秒AEST”选择“AEST”别的地方我的应用程序。

我想这个转换成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

所以,我使用的是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
}

NB StringBuilder是一个内部类,它并没有为这个问题的目的,无所谓。

更多信息:

我知道,这可以很容易地使用升压,等人来完成。 但是,这是不是和选项。 我需要它在数学上进行,或使用C或C ++标准功能,或它们的组合。

timegm似乎解决这个问题,但是,它并不出现在C / POSIX标准的一部分。 该代码目前被编译在多个平台(Linux,OSX,Windows中,的iOS,安卓(NDK)),所以我需要找到一种方法,使其在所有这些平台上工作,即使解决方案涉及#ifdef $PLATFORM类型的东西。

Answer 1:

这让我想扔在我的嘴里一点点,但你可以将其转换为一个字符串strftime()在字符串中更换时区,然后把它转换回来strptime()和成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

不过,我会强烈建议你说服是推动毕竟是一个选择的权力。 升压具有自定义时区的大力支持。 有迹象表明,这样做优雅以及其他库。



Answer 2:

如果你想要的是转换struct tm在UTC给一个time_t ,那么你可以做这样的:

#include <time.h>

time_t utc_to_time_t(struct tm* timeinfo)
{
    tzset(); // load timezone information (this can be called just once)

    time_t t = mktime(timeinfo);
    return t - timezone;
}

这基本上转换UTC时间time_t ,就好像给定的时间是本地的,然后应用时区修正的结果,使其恢复到UTC。

测试在海合会/ Cygwin和Visual Studio 2010中。

我希望这有帮助!

更新 :由于你非常好指出,我的解决方案上面可以返回time_t的值是一个小时,当查询日期的日光节约时间的状态是一个比当前时间不同。

该问题的解决方案是有一个附加功能,它可以告诉你,如果日期落在DST区域与否,并使用与当前DST标志以调整回来的时候mktime 。 这其实是很容易做到。 当你调用mktime()你只需要设置tm_dst成员为-1,然后系统将尽其所能在给定的时间内为您找出DST。 假设我们相信这个系统,那么你可以使用这些信息来应用校正:

#include <time.h>

time_t utc_to_time_t(struct tm* timeinfo)
{
    tzset(); // load timezone information (this can be called just once)

    timeinfo->tm_isdst = -1; // let the system figure this out for us
    time_t t = mktime(timeinfo) - timezone;

    if (daylight == 0 && timeinfo->tm_isdst != 0)
        t += 3600;
    else if (daylight != 0 && timeinfo->tm_isdst == 0)
        t -= 3600;
    return t;
}


Answer 3:

如果你在Linux或其它UNIX或类似UNIX的系统,那么你可能有一个timegm功能,你想要做什么。 所链接的手册页有便携式实施方案,所以你可以自己做到这一点。 在Windows中,我知道没有这样的功能。



Answer 4:

打我的头这几天试着去后timegm(1)功能:Android设备上工作(不与一艘船),我终于发现了这个简单而优雅的解决方案,它精美的作品:

time_t timegm( struct tm *tm ) {
  time_t t = mktime( tm );
  return t + localtime( &t )->tm_gmtoff;
}

我不明白为什么这不会是一个合适的跨平台解决方案。

我希望这有帮助!



Answer 5:

似乎有一个简单的解决方案:

#include <time64.h>
time_t timegm(struct tm* const t) 
{
  return (time_t)timegm64(t);
}

其实我也没testet但如果真的它的作品,因为我还有位移植做的,但它编译。



Answer 6:

这里是我的解决方案:

#ifdef WIN32
#   define timegm _mkgmtime
#endif

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;

return timegm(&timeinfo);

这应该既为UNIX和Windows



文章来源: Convert date and time numbers to time_t AND specify the timezone