How do I find the current system timezone?

2019-01-07 18:10发布

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?

12条回答
做自己的国王
2楼-- · 2019-01-07 18:57

I've been working on a free, open source C++11/14 library which addresses this question in a single line of code:

std::cout << date::current_zone()->name() << '\n';

It is meant to be portable across all recent flavors of Linux, macOS and Windows. For me this program outputs:

America/New_York

If you download this library, and it doesn't work you, bug reports are welcome.

查看更多
霸刀☆藐视天下
3楼-- · 2019-01-07 18:59

Here's code that works for most versions of Linux.

#include <iostream>
#include <time.h>
#include <unistd.h>
#include <stdio.h>
#include <sys/stat.h>
using namespace std;

void main()
{
    char filename[256];
    struct stat fstat;
    int status;

    status = lstat("/etc/localtime", &fstat);
    if (S_ISLNK(fstat.st_mode))
    {
        cout << "/etc/localtime Is a link" << endl;
        int nSize = readlink("/etc/localtime", filename, 256);
        if (nSize > 0)
        {
            filename[nSize] = 0;
            cout << "    linked filename " << filename << endl;
            cout << "    Timezone " << filename + 20 << endl;
        }
    }
    else if (S_ISREG(fstat.st_mode))
        cout << "/etc/localtime Is a file" << endl;
} 
查看更多
Fickle 薄情
4楼-- · 2019-01-07 18:59

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?

查看更多
Viruses.
5楼-- · 2019-01-07 19:00

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 and tzdata2017a.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.

查看更多
Fickle 薄情
6楼-- · 2019-01-07 19:02

The libc accesses the Olson database when tzset is called, and uses simplified time zones afterwards. tzset looks at the TZ 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, then readlink("/etc/localtime"), is the most reliable way to match the libc's tzset 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).

查看更多
▲ chillily
7楼-- · 2019-01-07 19:03

On Linux, I need to find the current timezone as an Olson 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.

查看更多
登录 后发表回答