Mam następujące liczby całkowite:data i czas konwersji liczb do time_t i określić strefę czasową
int y, mon, d, h, min, s;
Ich wartości to: 2012
, 06
, 27
, 12
, 47
, 53
odpowiednio. Chcę reprezentować czas daty "2012/06/27 12:47:53 UTC", jeśli wybrałem opcję "UTC" w innym miejscu w moim zgłoszeniu lub "2012/06/27 12:47:53 AEST", jeśli wybrałem "AEST" gdzie indziej w mojej aplikacji.
Chcę konwersji na time_t
, a oto kod, że jestem obecny używając do tego:
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
Więc używam tm struct
i mktime
, jednak to nie działa dobrze, ponieważ zawsze przyjmuje moją lokalną strefę czasową.
Jaki jest prawidłowy sposób wykonania tej czynności?
Poniżej znajduje się rozwiązanie, które do tej pory wymyśliłem. To w zasadzie czyni jedną z trzech rzeczy:
- Jeśli UNIX, wystarczy użyć
timegm
- jeśli nie UNIX
- Albo, czy matematyki stosując różnicę między czasem UTC epoki i lokalnym epoki jako offset
- Rezerwacja: Matematyka może być niepoprawna
- Lub ustaw tymczasowo zmienną środowiskową "TZ" na UTC
- Rezerwacja: wyłączy się, jeśli/kiedy ten kod musi być wielowątkowy
- Albo, czy matematyki stosując różnicę między czasem UTC epoki i lokalnym epoki jako offset
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
jest klasą wewnętrzną, nie ma znaczenia dla celów tego pytania.
Więcej informacji:
Wiem, że można to zrobić łatwo za pomocą impuls, et al. Ale to NIE jest i opcja. Muszę to zrobić matematycznie lub używając standardowej funkcji c lub C++ lub ich kombinacji.
timegm
wydaje się rozwiązać ten problem, jednak nie wydaje się być częścią standardu C/POSIX. Ten kod jest obecnie kompilowany na wielu platformach (Linux, OSX, WIndows, iOS, Android (NDK)), więc muszę znaleźć sposób, aby działał na wszystkich tych platformach, nawet jeśli rozwiązanie polega na wpisywaniu rzeczy typu #ifdef $PLATFORM
.
Czy masz dostępne inne biblioteki? Czy masz listę stref czasowych i ich przesunięć? – Zac
@Zac Nie, nie chcę używać żadnych bibliotek, ponieważ ponoszą one znaczne koszty rozwoju podczas kompilacji krzyżowej. Również nie, nie mam listy stref czasowych i ich przesunięć. Spójrz na moją aktualizację powyżej, mam tam sposób, aby obliczyć przesunięcia strefy czasowej - czy to wygląda dla Ciebie? – bguiz
Możesz przekonwertować go na ciąg znaków za pomocą 'strftime()', zamień strefę czasową w łańcuchu, a następnie przekonwertuj ją z powrotem na 'mktime (strptime())', ale chciałbym tylko przekonać moce, które są tym boostem w rzeczywistości opcja. – smocking