On Linux, I need to find the currently configured timezone as an Olson 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 Olson 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 Olson location, on Linux?
I've been working on a free, open source C++11/14 library which addresses this question in a single line of code:
It is meant to be portable across all recent flavors of Linux, macOS and Windows. For me this program outputs:
If you download this library, and it doesn't work you, bug reports are welcome.
Here's code that works for most versions of Linux.
According to this page, it looks like if you
#include <time.h>
it will declare the following.Does that give you the information that you need?
Since tzselect was not mentioned by anyone and you do need a nearly goof-proof solution, work with what Olson did. Get the tzcode and tzdata files from elsie, plus tab files.
ftp://elsie.nci.nih.gov
In March 2017, the correct location to download from would be ftp://ftp.iana.org/tz/releases (and download
tzcode2017a.tar.gz
andtzdata2017a.tar.gz
).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.
The libc accesses the Olson database when tzset is called, and uses simplified time zones afterwards.
tzset
looks at theTZ
environment variable first, and falls back to parsing the binary data in/etc/localtime
.At first systemd standardised on having the Olson time zone name in
/etc/timezone
, Debian-style. After systemd 190 and the /usr merge, systemd only reads and updates/etc/localtime
, with the extra requirement that the file be a symlink to/usr/share/zoneinfo/${OLSON_NAME}
.Looking at
TZ
, thenreadlink("/etc/localtime")
, is the most reliable way to match the libc'stzset
logic and still keep symbolic Olson names. For systems that don't follow the systemd symlink convention, reading/etc/timezone
(and possibly checking that/usr/share/zoneinfo/$(</etc/timezone)
is the same as/etc/localtime
) is a good fallback.If you can live without symbolic names, parsing the
/etc/localtime
tzfile is as portable as it gets, though a lot more complex. Reading just the last field gets you a Posix time zone (for example:CST5CDT,M3.2.0/0,M11.1.0/1
), which can interoperate with a few time-handling libraries, but drops some of the metadata (no historical transition info).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.