I am writing some code that will run on multiple intercommunicating systems. I was using time() to get time_t, but this was causing problems with time zone differences between the systems, so I want to get time_t in GMT. I've been looking through the time.h functions, but it is unclear to me how I can be sure that I will get the time correctly. This is what I've come up with so far:
time_t t = time();
struct tm *gtm = gmtime(&t);
time_t gt = mktime(gtm);
Now, this seems to get the correct answer on my machine, but I want to know if it will work universally before I push it out, even if the other machines' clocks are set to local time or GMT or in different time zones or whatever. The reason I am concerned is because of mktime. In the description, it says that it interprets the tm struct as "a calendar time expressed in local time." That sounds to me like it will not be returning the GMT time, though it seems to be on my machine. Also, when I print out gt, it is 4 hours ahead of t, which seems right. But if I run the following:
time_t t = time();
struct tm *gtm = gmtime(&t);
struct tm *ltm = localtime(&t);
printf("%d, %d\n", gtm->tm_hour, ltm->tm_hour);
the hours are the same and are local time, which is not what I expected.
For the record, in another answer, I saw reference to timegm(), which sounds perfect, but it does not exist on my system.
So in short, how do I get time_t in GMT on any Windows machine in C?
Edit: removed msvcrt tag that was added, as I am not using the msvcrt.
time_t is always in UTC, by definition. So time() does what you want. Timezones only come into play when you are converting between time_t and broken-down time representation.
If you have the time in UTC in broken-down time representation, you need to temporarily switch the timezone to UTC, use mktime() to convert to a time_t, and switch the timezone back, like this:
time_t convert_utc_tm_to_time_t (struct tm *tm)
{
char *tz;
time_t result;
/* temporarily set timezone to UTC for conversion */
tz = getenv("TZ");
if (tz) {
tz = strdup (tz);
if (!tz) {
// out of memory
return -1;
}
}
setenv("TZ", "", 1);
tzset();
result = mktime (tm);
/* restore timezone */
if (tz) {
setenv("TZ", tz, 1);
free (tz);
}
else {
unsetenv("TZ");
}
tzset();
return result;
}
Use gmtime()
as you want your time figure in GMT. localtime
will report the time in the local time zone, and will only by GMT when the local time zone is also GMT.
Windows has its own code and data structures for dealing with time and time zones. To get GMT using windows specific c code, you can use GetSystemTime()
. You can find more information about it at this URL: http://msdn.microsoft.com/en-us/library/windows/desktop/ms724390(v=vs.85).aspx
Please note that Windows uses its own date/time structure, but that is also detailed with the GetSystemTime()
and its related pages. In short, GetSystemTime()
retrieves the current system date and time, expressed in Coordinated Universal Time (UTC) (also known as GMT). It uses the Windows structure SYSTEMTIME which is a structure with many elements of time.
Looking at the first part of your code:
time_t t = time();
struct tm *gtm = gmtime(&t);
time_t gt = mktime(gtm);
You're setting gt
to a bogus value. When calling mktime
, you need to pass in a struct tm*
that is in local time. time_t
is defined as the number of seconds since January 1, 1970, 00:00 UTC. Unless you're currently in the +0000 time zone, the return value from your call to mktime
will represent some time a few hours in the past or future.
As for the second part:
time_t t = time();
struct tm *gtm = gmtime(&t);
struct tm *ltm = localtime(&t);
printf("%d, %d\n", gtm->tm_hour, ltm->tm_hour);
I think gmtime
and localtime
are returning the same buffer in memory, which might be why you're getting the same two hours in your output. The second call is overwriting the output of the first. Try this instead:
struct tm gtm, ltm;
struct tm *tm_buf;
time_t t = time();
tm_buf = gmtime(&t);
memcpy(>m, tm_buf, sizeof(struct tm));
tm_buf = localtime(&t);
memcpy(<m, tm_buf, sizeof(struct tm));
printf("%d, %d\n", gtm.tm_hour, ltm.tm_hour);
Note that for such a trivial conversion, you can use your own function. There is a copy here of the mkgmtime()
https://github.com/m2osw/snapcpp/blob/master/snapwebsites/libsnapwebsites/src/snapwebsites/mkgmtime.c
This allows you to convert a struct tm
to a time_t
value ignoring any timezone information. Not including the comments, this is probably less than 100 lines of code, so very easy to add it to your project.