32

What's the best way to convert datetimes between local time and UTC in C/C++?

By "datetime", I mean some time representation that contains date and time-of-day. I'll be happy with time_t, struct tm, or any other representation that makes it possible.

My platform is Linux.

Here's the specific problem I'm trying to solve: I get a pair of values containing a julian date and a number of seconds into the day. Those values are in GMT. I need to convert that to a local-timezone "YYYYMMDDHHMMSS" value. I know how to convert the julian date to Y-M-D, and obviously it is easy to convert seconds into HHMMSS. However, the tricky part is the timezone conversion. I'm sure I can figure out a solution, but I'd prefer to find a "standard" or "well-known" way rather than stumbling around.


A possibly related question is Get Daylight Saving Transition Dates For Time Zones in C

Community
  • 1
  • 1
Kristopher Johnson
  • 81,409
  • 55
  • 245
  • 302

3 Answers3

24

You're supposed to use combinations of gmtime/localtime and timegm/mktime. That should give you the orthogonal tools to do conversions between struct tm and time_t.

For UTC/GMT:

time_t t;
struct tm tm;
struct tm * tmp;
...
t = timegm(&tm);
...
tmp = gmtime(t);

For localtime:

t = mktime(&tm);
...
tmp = localtime(t);

All tzset() does is set the internal timezone variable from the TZ environment variable. I don't think this is supposed to be called more than once.

If you're trying to convert between timezones, you should modify the struct tm's tm_gmtoff.

Rick C. Petty
  • 941
  • 5
  • 8
  • Thanks! I knew something like timegm() had to exist, but didn't know the name. – Kristopher Johnson Apr 17 '09 at 19:22
  • Glad to help. Time conversion can be a rather tricky area. It's usually a good idea to store everything in UTC whenever possible. Converting between timezones is even trickier! – Rick C. Petty Apr 17 '09 at 19:24
  • 1
    Yeah, unfortunately the system's designers chose local time as the "standard" timestamp format for the messaging protocol. I hate it when people do that. – Kristopher Johnson Apr 17 '09 at 19:34
  • 4
    There is no timegm in Windows. – stepancheg Dec 21 '09 at 11:42
  • I'll likely ask this as a separate question (digging through questions that mention `tm_gmtoff` now, and the list is pretty short), but: Could you perhaps give an example of modifying tm_gmtoff and having that do anything useful? It doesn't seem to change the output of, e.g, `strftime(buf, BUFSIZ, "%FT%T%z (%+ %z)", tm);`, when I do it. ?? – lindes Feb 23 '11 at 08:23
  • It doesn't appear that tm_gmtoff is a standard member of struct tm. http://www.cplusplus.com/reference/clibrary/ctime/tm – Oscar Nov 27 '11 at 23:40
  • As mentioned by @stepancheg, `timegm` is a non-standard GNU/BSD extension and is not available on Windows, etc. The [Linux man page for `timegm`](http://linux.die.net/man/3/timegm) shows a portable approach based on changing `TZ` in the environment. Unfortunately, this too isn't immediately portable to Windows, due to its lack of `setenv`. – Trevor Robinson Jan 15 '13 at 21:14
  • On Windows you can use `_mkgmtime(&tm);`, it interprets the `struct tm` to be in UTC rather than in your current TZ. – dash-tom-bang Jul 26 '16 at 20:25
19

If on Windows, you don't have timegm() available to you:

struct tm *tptr;
time_t secs, local_secs, gmt_secs;
time( &secs );  // Current time in GMT
// Remember that localtime/gmtime overwrite same location
tptr = localtime( &secs );
local_secs = mktime( tptr );
tptr = gmtime( &secs );
gmt_secs = mktime( tptr );
long diff_secs = long(local_secs - gmt_secs);

or something similar...

  • Shouldn't `secs` and `gmt_secs` be identical? I would think you could simplify this quite a bit. – jowo Jan 17 '12 at 07:18
  • 1
    I tried this. I tried to verify it by testing the result against the inverse. For some times it works, and for others, it is off by an hour. I'm guessing that's because DST starts and stops at different dates in the different locations I'm trying (using GMT, but converting against PST localtime). Is there any way to compensate for that? – Chris Westin Feb 21 '12 at 23:32
  • 1
    This version, which uses the Boost POSIX time library, seems to work despite differing DST dates across timezones: http://stackoverflow.com/a/6849810/857029 . – Chris Westin Feb 22 '12 at 01:27
3

If you need to worry about converting date/time with timezone rules, you might want to look into ICU.

chrish
  • 2,352
  • 1
  • 17
  • 32