I'm looking for something that I presumed would be very simple - given local Unix time in a specific time zone (specified as a string, e.g., "America/New_York" - note that's not my local time), get the corresponding time value in GMT. I.e., something along the lines of
time_t get_gmt_time(time_t local_time,
const char* time_zone);
As deceptively simple as it sounds, the closest I could find was the following code snippet from timegm's man page:
#include <time.h>
#include <stdlib.h>
time_t
my_timegm(struct tm *tm)
{
time_t ret;
char *tz;
tz = getenv("TZ");
setenv("TZ", "", 1);
tzset();
ret = mktime(tm);
if (tz)
setenv("TZ", tz, 1);
else
unsetenv("TZ");
tzset();
return ret;
}
There gotta be a better way than this belligerently not thread-safe abomination, right? Right??
Open the Activities overview and start typing Settings. Click on Settings. Click Date & Time in the sidebar to open the panel. If you have the Automatic Time Zone switch set to on, your time zone should update automatically if you have an internet connection and the location services feature is enabled.
Select the Terminal program from your Linux programs, or press Ctrl + Alt + T on your keyboard. Enter the time zone menu command. Depending on your Linux distribution, this command will vary: Ubuntu and Mint - sudo dpkg-reconfigure tzdata followed by the admin/user password.
TimeZoneInfo timeZone = TimeZoneInfo. FindSystemTimeZoneById("Eastern Standard Time"); var time = timeZoneInfo. ConvertTime(gmTime, timeZone);
Wanted to add a bit more detail here.
If you try the following:
#include <stdio.h> #include <time.h> /* defines 'extern long timezone' */ int main(int argc, char **argv) { time_t t, lt, gt; struct tm tm; t = time(NULL); lt = mktime(localtime(&t)); gt = mktime(gmtime(&t)); printf( "(t = time(NULL)) == %x,\n" "mktime(localtime(&t)) == %x,\n" "mktime(gmtime(&t)) == %x\n" "difftime(...) == %f\n" "timezone == %d\n", t, lt, gt, difftime(gt, lt), timezone); return 0; }
you'll notice that timezone conversions make sure that:
mktime(localtime(t)) == t
, andmktime(gmtime(t)) == t + timezone
,difftime(mktime(gmtime(t)), mktime(localtime(t))) == timezone
tzset()
or the invocation of any timezone conversion function).Example output of the above:
$ TZ=GMT ./xx (t = time(NULL)) == 4dd13bac, mktime(localtime(&t)) == 4dd13bac, mktime(gmtime(&t)) == 4dd13bac difftime(...) == 0.000000 timezone == 0 $ TZ=EST ./xx (t = time(NULL)) == 4dd13baf, mktime(localtime(&t)) == 4dd13baf, mktime(gmtime(&t)) == 4dd181ff difftime(...) == 18000.000000 timezone == 18000 $ TZ=CET ./xx (t = time(NULL)) == 4dd13bb2, mktime(localtime(&t)) == 4dd13bb2, mktime(gmtime(&t)) == 4dd12da2 difftime(...) == -3600.000000 timezone == -3600
In that sense, you're attempting to "do it backwards" - time_t
is treated as absolute in UN*X, i.e. always relative to the "EPOCH" (0:00 UTC on 01/01/1970).
The difference between UTC and the current timezone (last tzset()
call) is always in the external long timezone
global.
That doesn't get rid of the environment manipulation uglyness, but you can save yourself the effort of going through mktime()
.
From tzfile(5), which documents the files in /usr/share/zoneinfo (on my system) in gruesome detail:
It seems that timezone uses tzfile internally, but glibc refuses to expose it to userspace. This is most likely because the standardised functions are more useful and portable, and actually documented by glibc.
Again, this is probably not what you're looking for (ie. an API), but the information is there and you can parse it without too much pain.
The problem with gmtime, localtime and their variants is the reliance on the TZ environment variable. The time functions first call tzset(void), which reads TZ to determine offsets DST, etc. If TZ is not set in the user's environment, (g)libc uses the system timezone. So if you have a local struct tm in, say, 'Europe/Paris' and your machine or environment is set to 'America/Denver', the wrong offset will be applied when you convert to GMT. All the time functions call tzset(void) which reads TZ to set char *tzname[2], long timezone (diff, in seconds, from GMT) and int daylight (boolean for DST). Setting these directly has no affect, because tzset() will overwrite them the next time you call localtime, etc.
I was faced with the same issue as 'igor' in the original question, while setenv works it seems problematic (re-entran?). I decided to look further to see if I could modify tzset (void) to tzset(char*) to explicitly set the above mentioned variables. Well, of course, that's just a bad idea... but in probing the glibc source and the IANA TZ database source, I came to the conclusion that the setenv approach ain't so bad.
First, setenv only modifies the process global 'char **environ' (not the calling shell, so the 'real' TZ is not affected). And, second, glibc actually puts a lock in setenv. The drawback is that setenv/tzset calls are not atomic, so another thread could conceivably write to TZ before the original thread call tzset. But a well-implemented application that uses threads should watch for that anyway.
It would be cool if POSIX defined tzset to take a char* for look up in the extensive IANA TZ database (and take NULL to mean, 'use the user or system TZ/), but failing that, setenv seems to be ok.
I really thought there was something in glib, but seem to have misremembered. I know you're probably looking for straight-up C code, but here's the best I've got:
I know that Python has some notion of timezones through a tzinfo
class - you can read about it in the datetime documentation. You can have a look at the source code for the module (in the tarball, it's in Modules/datetime.c) - it appears to have some documentation, so maybe you can get something out of it.
Similar to the Python answer, I can show you what R does:
R> now <- Sys.time() # get current time
R> format(now) # format under local TZ
[1] "2009-08-03 18:55:57"
R> format(now,tz="Europe/London") # format under explicit TZ
[1] "2009-08-04 00:55:57"
R> format(now,tz="America/Chicago") # format under explicit TZ
[1] "2009-08-03 18:55:57"
R>
but R uses an internal representation that extends the usual struct tm
--- see R-2.9.1/src/main/datetime.c.
Still, this is a hairy topic and it would be nice if it were the standard library. As it isn't maybe your best bet is to use Boost Date_Time (example)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With