Convert localtime in seconds since epoch to UTC

1.4k views Asked by At

In my system I have a PC (Linux, in case it matters) which keeps RTC time in UTC, making my localtime timezone specific. In PC code, I get UTC time as seconds since epoch using

struct timespec tv; 
clock_gettime(CLOCK_REALTIME, &tv);
double time = (tv.tv_nsec / 1000000000.0) + tv.tv_sec;
return time;

I also have a 3rd party network device which provides its time also as seconds from epoch, but it does so using localtime instead of UTC time. This is a problem because, when I print the two timestamps in an interleaved log with timestamps from PC and this device, even though the two clocks show the same localtime, the timestamps are off.

Let's assume that the timezone settings (UTC offset and daylight savings specifications) are the same between the PC and this device. How would I take the seconds since epoch provided by the device (in localtime) and convert it to seconds since epoch in UTC? In other words, what the programmatic (in C) way to apply PC timezone settings to a seconds since epoch when that number is in localtime?

Here is my attempt at converting the 3rd party device localtime based seconds since epoch to UTC based seconds since epoch.

#include <stdio.h>
#include <time.h>

int main(void)
{
  // The following epoch timestamps were converted to human time via https://www.epochconverter.com/
  time_t device_rawtime = 1568133906.065000; // if treated as GMT:       Tuesday, September 10, 2019 4:45:06.065 PM
  time_t pc_rawtime     = 1568151907.454432; // if treated as localtime: Tuesday, September 10, 2019 4:45:07.454 PM GMT-05:00 DST
  struct tm  ts; 
  char       buf[80];

  ts = *gmtime(&device_rawtime);
  strftime(buf, sizeof(buf), "%a %Y-%m-%d %H:%M:%S %Z", &ts);
  time_t converted = mktime(&ts);
  printf("Device rawtime=%ld which is PC localtime %s ==> UTC based rawtime=%ld (pc was %ld)\n", device_rawtime, buf, converted, pc_rawtime);
  return 0;
}

The above does not work. It prints

Device rawtime=1568133906 which is PC localtime Tue 2019-09-10 16:45:06 GMT ==> UTC based rawtime=1568155506 (pc was 1568151907)

As you can see, the converted device timestamp does not equal PC timestamp. How should this be done?

1

There are 1 answers

0
Armali On BEST ANSWER

I agree that it's likely due to daylight savings. But the question is how should I be accounting for that?

The relevant information is found in man mktime:

The value specified in the tm_isdst field informs mktime() whether or not daylight saving time (DST) is in effect for the time supplied in the tm structure: a positive value means DST is in effect; zero means that DST is not in effect; and a negative value means that mktime() should (use timezone information and system databases to) attempt to determine whether DST is in effect at the specified time.

On return from gmtime(&device_rawtime), ts.tm_isdst is set to zero, since UTC taken by gmtime() is never daylight saving. So, when mktime(&ts) is called, it converts the time structure with the information that DST is not in effect, thus we get a converted time value which is 3600 seconds too high. To correctly account for DST, setting

ts.tm_isdst = -1

before calling mktime(&ts) is sufficient.