将日期和时间数字转换为time_t并指定时区 [英] Convert date and time numbers to time_t AND specify the timezone

查看:182
本文介绍了将日期和时间数字转换为time_t并指定时区的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有以下整数:

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

它们的值是: 2012 06 27 12 47 53 。如果我在应用程序中的其他位置选择了 UTC,我想代表 2012/06/27 12:47:53 UTC的日期时间,或者如果我选择 2012/06/27 12:47:53 AEST在应用程序的其他位置选择了 AEST。

Their values are: 2012, 06, 27, 12, 47, 53 respectively. I want to represent the date time of "2012/06/27 12:47:53 UTC" if I have selected 'UTC' somewhere else in my application, or "2012/06/27 12:47:53 AEST" if I have selected 'AEST' somewhere else in my application.

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

I want to convert this into a time_t, and here's the code that I am current using to do so:

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 struct mktime ,但是效果不佳,因为它始终采用我的本地时区。

So I am using a tm struct and mktime, however this is not working well, because it is always assuming my local time-zone.

执行此操作的正确方法是什么?

What is the correct way of doing this?

因此,到目前为止,我已经提出了以下解决方案。
它基本上执行以下三项操作之一:

So below is the solution that I have come up with so far. It basically does one of three things:


  1. 如果是UNIX,只需使用 timegm

  2. 如果不是UNIX
  1. If UNIX, simply use timegm
  2. If not UNIX

  1. 要么使用UTC历元和本地历元之间的差作为偏移量$ b $进行数学运算b

    • 预订:数学可能不正确


  • 预订:如果/当此代码需要多线程时会跳开




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
}

N.B。 StringBuilder 是一个内部类,对于此问题的使用无关紧要。

N.B. StringBuilder is an internal class, it doesn't matter for the purposes of this question.

更多信息:

我知道可以使用boost等轻松完成此操作。但这不是选择。我需要用数学方法来完成,或者使用ac或c ++标准函数,或其组合。

I know that this can be done easily using boost, et al. But this is NOT and option. I need it to be done mathematically, or using a c or c++ standard function, or combinations thereof.

timegm 出现但是,解决此问题的方法似乎并不属于C / POSIX标准。该代码当前在多个平台(Linux,OSX,WINDOWS,iOS,Android(NDK))上编译,因此即使解决方案涉及 #ifdef $ PLATFORM 键入内容。

timegm appears to solve this problem, however, it doesn't appear to part of the C / POSIX standard. This code currently is compiled on multiple platforms (Linux, OSX, WIndows, iOS, Android (NDK)), so I need to find a way to make it work across all of these platforms, even if the solution involves #ifdef $PLATFORM type things.

推荐答案

这让我想吐在嘴里一点点,但是您可以使用 strftime()将其转换为字符串,替换字符串中的时区,然后使用 strptime( )并与 mktime()一起放入 time_t 。详细信息:

It makes me want to throw up in my mouth a little bit, but you could convert it to a string with strftime(), replace the timezone in the string and then convert it back with strptime() and into a time_t with mktime(). In detail:

#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

但是,我强烈建议您说服权力毕竟,提升是一个选择。 Boost对自定义时区提供了强大的支持。还有其他一些库也可以很好地做到这一点。

However, I would highly recommend you convince the powers that be that boost is an option after all. Boost has great support for custom timezones. There are other libraries that do this elegantly as well.

这篇关于将日期和时间数字转换为time_t并指定时区的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆