views:

175

answers:

5

On Linux, I need to find the currently configured timezone as an Olsen location. I want my (C or C++) code to be portable to as many Linux systems as possible.

For example. I live in London, so my current Olsen location is "Europe/London". I'm not interested in timezone IDs like "BST", "EST" or whatever.

Debian and Ubuntu have a file /etc/timezone that contains this information, but I don't think I can rely on that file always being there, can I? Gnome has a function oobs_time_config_get_timezone() which also returns the right string, but I want my code to work on systems without Gnome.

So, what's the best general way to get the currently configured timezone as an Olsen location, on Linux?

A: 

According to this page, it looks like if you #include <time.h> it will declare the following.

void tzset (void);
extern char *tzname[2];
extern long timezone;
extern int daylight;

Does that give you the information that you need?

torak
Does anyone know why when I delete the space before the 't' in time.h above "<time.h>" dissapears?
torak
because inline code should be specified with backticks, not `<code>` blocks
Evan Teran
@Evan Teran: Thanks. Up 'til now <code> has just worked for me. Further, I just realised that I was effectively embeding a <time.h> html tag. Without a </time.h> tag no less. D'oh!
torak
No. Did you actually read that man page?
alex tingle
+2  A: 

There is no standard c or c++ function for this. However, GNU libc has an extention. its struct tm has two extra members:

long tm_gmtoff;           /* Seconds east of UTC */
const char *tm_zone;      /* Timezone abbreviation */

This means that if you use one of the functions which populates a struct tm (such as localtime or gmtime) you can use these extra fields. This is of course only if you are using GNU libc (and a sufficiently recent version of it).

Also many systems have a int gettimeofday(struct timeval *tv, struct timezone *tz); function (POSIX) which will fill in a struct timezone. This has the following fields:

struct timezone {
    int tz_minuteswest;     /* minutes west of Greenwich */
    int tz_dsttime;         /* type of DST correction */
};

Not exactly what you asked for, but close...

Evan Teran
That's all correct, but as you say, it doesn't answer my question.
alex tingle
+6  A: 

It's hard to get a reliable answer. Relying on things like /etc/timezone may be the best bet.

(The variable tzname and the tm_zone member of struct tm, as suggested in other answers, typically contains an abbreviation such as GMT/BST etc, rather than the Olson time string as requested in the question).

  • On Debian-based systems (including Ubuntu), /etc/timezone is a file containing the right answer.
  • On some Redhat-based systems (including at least some versions of CentOS, RHEL, Fedora), you can get the required information using readlink() on /etc/localtime, which is a symlink to (for example) /usr/share/zoneinfo/Europe/London.
  • OpenBSD seems to use the same scheme as RedHat.

However, there are some issues with the above approaches. The /usr/share/zoneinfo directory also contains files such as GMT and GB, so it's possible the user may configure the symlink to point there.

Also there's nothing to stop the user copying the right timezone file there instead of creating a symlink.

One possibility to get round this (which seems to work on Debian, RedHat and OpenBSD) is to compare the contents of the /etc/localtime file to the files under /usr/share/zoneinfo, and see which ones match:

eta:~% md5sum /etc/localtime
410c65079e6d14f4eedf50c19bd073f8  /etc/localtime
eta:~% find /usr/share/zoneinfo -type f | xargs md5sum | grep 410c65079e6d14f4eedf50c19bd073f8
410c65079e6d14f4eedf50c19bd073f8  /usr/share/zoneinfo/Europe/London
410c65079e6d14f4eedf50c19bd073f8  /usr/share/zoneinfo/Europe/Belfast
410c65079e6d14f4eedf50c19bd073f8  /usr/share/zoneinfo/Europe/Guernsey
410c65079e6d14f4eedf50c19bd073f8  /usr/share/zoneinfo/Europe/Jersey
410c65079e6d14f4eedf50c19bd073f8  /usr/share/zoneinfo/Europe/Isle_of_Man
...
...

Of course the disadvantage is that this will tell you all timezones that are identical to the current one. (That means identical in the full sense - not just "currently at the same time", but also "always change their clocks on the same day as far as the system knows".)

Your best bet may be to combine the above methods: use /etc/timezone if it exists; otherwise try parsing /etc/localtime as a symlink; if that fails, search for matching timezone definition files; if that fails - give up and go home ;-)

(And I have no idea whether any of the above applies on AIX...)

psmears
The OP wanted this for Linux mostly. AIX does not play well in the portability arena on anything remotely related to system congfiguration, including time/date settings. IMO.
jim mcnamara
The OP doesn't seem to say anything about AIX, so solving it there is a non-requirement.
Donal Fellows
A: 

On Linux, I need to find the current timezone as an Olsen location. I want my (C or C++) code to be portable to as many Linux systems as possible.

If you want to be portable, then use only GMT internally. Due to multi-user heritge, *NIX system clock is normally is in GMT and there is no system wide timezone - because different users connected to the system might be living in different timezones.

The user specific timezone is reflected in TZ environment variable and you might need to use that only when converting internal date/time into the user readable form. Otherwise, localtime() takes care of it automatically for you.

Dummy00001
This doesn't answer my question. TZ is rarely set, and even less often set to the Olsen location. The rest of your answer is either irrelevant or incorrect - there is no such timezone as GMT, you mean UTC. (I'll give you a bonus upvote if you can explain the difference :)
alex tingle
GMT is UTC+0/WET and might have DST. UTC is a universal coordinated time. Or whatever. I realized too late that I have answered wrong question. But TZ *has* to be set for local time to work properly. And I frankly hasn't seen a system where TZ wasn't set. E.g. our application requires TZ and is portable across many systems and across many timezones: we have business cases where we have to cover countries which span multiple timezones.
Dummy00001
OK, fair enough. Edit your answer and I'll upvote it. Your definition of GMT is not correct though. GMT is (was) the mean solar time at Greenwich. So GMT was defined in terms of astronomical observations, while UTC uses an atomic clock as its yardstick.
alex tingle
How the times are derived isn't relevant actually. I wrote GMT in the post and it looks out of context - because I removed the reference to the `gmtime()`. It might return UTC, but it is still **gm**time(). The downvote is OK as it is not an answer to your question.
Dummy00001
@Dummy00001: there are plenty of systems with no `TZ` environment var set. Mine for example (Gentoo Linux).
Evan Teran
Just double checked. HP-UX, Solaris and Linux all save default timezone in a default location (/etc/default/tz, /etc/default/init and /etc/localtime respectively) and work fine without $TZ. But AIX wants to have $TZ and otherwise dumbly give UTC if no $TZ is set. That's apparently the reason why our software puts the requirement to have the $TZ set.
Dummy00001
@Alex - getting snippy about calendrics and timezones is not gonna win you any friends, especially when when your question implies you never read basic stuff -- tzselect.ksh. Wanna get funny - then what exact date did Santa Fe NM go Gregorian?
jim mcnamara
alex tingle
A: 

Since tzselect was not mentioned by anyone and you do need a nearly goof-proof solution, work with what Olsen did. get the tzcode and tzdata files from elsie, plus tab files.

ftp://elsie.nci.nih.gov

Then get tzselect.ksh from the glibc download. Then you can see how to reverse engineer timezones. One sticking point: you WILL sometimes have to ask what country and city the linux box is in. You can serialize that data if you want, and then verify it against the timezone data you can find.

There is no way to do this reliably all the time without the possibility of user intervention, for example, as part of program installation.

Good luck on Arizona in general, and in Western Indiana.... hopefully your code is going to run elsewhere.

jim mcnamara
tzselect is a user interface that allows the user to choose a timezone. It tells me nothing about the configured timezone on the local machine.
alex tingle
Wrong - it also allows you to see how to reverse engineer the data you need. Based on user input, when there are no other options.
jim mcnamara