Преобразовать числа даты и времени в time_t И указать часовой пояс

У меня есть следующие целые числа:

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

Их значения: 2012 , 06, 27, 12, 47, 53соответственно. Я хочу представить дату и время «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 structи mktime, однако это не работает, потому что всегда используется мой местный часовой пояс.

Как правильно это сделать?

Ниже приведено решение, которое я нашел на данный момент. В основном он делает одну из трех вещей:

  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
}

N.B. StringBuilder— это внутренний класс, для целей данного вопроса это не имеет значения.

Дополнительная информация:

Я знаю, что это можно легко сделать с помощью boost и др.Но это НЕ вариант. Мне нужно, чтобы это было сделано математически или с использованием стандартной функции c или c++ или их комбинации.

timegm, по-видимому, решает эту проблему, однако не является частью стандарта C/POSIX. Этот код в настоящее время скомпилирован для нескольких платформ (Linux, OSX, WIndows, iOS, Android (NDK)), поэтому мне нужно найти способ заставить его работать на всех этих платформах, даже если решение включает в себя #ifdef Тип $PLATFORM.

8
задан BartoszKP 11 December 2014 в 12:50
поделиться