将日期和时间数字转换为time_t并指定时区 [英] Convert date and time numbers to time_t AND specify the timezone
问题描述
我有以下整数:
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:
- 如果是UNIX,只需使用
timegm
- 如果不是UNIX
- If UNIX, simply use
timegm
- If not UNIX
- 要么使用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屋!