Convert date and time numbers to time_t AND specify the timezone

天大地大妈咪最大 提交于 2019-12-03 13:08:18

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

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.

If all you want is to convert a struct tm given in UTC to a time_t then you can do it like this:

#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;
}

This basically converts the UTC time to time_t as if the given time was local, then applies a timezone correction to the result to bring it back to UTC.

Tested on gcc/cygwin and Visual Studio 2010.

I hope this helps!

Update: As you very well pointed out, my solution above may return time_t value that is one hour off when the daylight time savings state of the queried date is different than the one for the current time.

The solution for that problem is to have an additional function that can tell you if a date falls in the DST region or not, and use that and the current DST flag to adjust the time returned by mktime. This is actually easy to do. When you call mktime() you just have to set the tm_dst member to -1 and then the system will do its best to figure out the DST at the given time for you. Assuming we trust the system on this, then you can use this information to apply a correction:

#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;
}

If you are on Linux or other UNIx or UNIX-like system then you might have a timegm function that does what you want. The linked manual page have a portable implementation so you can make it yourself. On Windows I know of no such function.

After beating my head against this for days trying to get a timegm(1) function that works on Android (which does not ship with one), I finally discovered this simple and elegant solution, which works beautifully:

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

I don't see why this wouldn't be a suitable cross-platform solution.

I hope this helps!

There seems to be a simpler solution:

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

Actually I have not testet yet if really it works, because I still have a bit of porting to do, but it compiles.

Here's my solution:

#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);

This should work both for unix and windows

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!