Home > Software engineering >  how to get current time zone city name like America/Los_Angeles
how to get current time zone city name like America/Los_Angeles

Time:01-01

I tried strftime, which has %z and %Z, but these are outputs like 0800 and PST. I'd like to get the longer name like America/Los_Angeles. Is there a function call to do this?

CodePudding user response:

There is not a standard way to do this until C 20, and even then only the latest MSVC has implemented it to date (gcc is getting close).

In C 20 there is a type std::chrono::time_zone which has a member function called name() which will return a string such as "America/Los_Angeles".

It might be used like this:

#include <chrono>
#include <iostream>

int
main()
{
    using namespace std;
    using namespace std::chrono;

    zoned_time local_now{"America/Los_Angeles", system_clock::now()};
    cout << local_now << " " << local_now.get_time_zone()->name() << '\n';
}

Which just output for me:

2022-12-31 07:34:41.482431 PST America/Los_Angeles

Or if your computer's local time zone is currently set to "America/Los_Angeles", then the zoned_time construction could look like this instead:

    zoned_time local_now{current_zone(), system_clock::now()};

If all you want is the time zone name, and not the current time, this can be further simplified to just:

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

Prior to C 20 the only way I'm aware of to get functionality like this is to use my free, open-source C 20 chrono preview library which will work with C 11/14/17.

CodePudding user response:

One way — I must admit, a decidedly imperfect way — is to read the symbolic link /etc/localtime:

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>

#define LOCALTIME "/etc/localtime"

int main()
{
    char buf[100];
    int r = readlink(LOCALTIME, buf, sizeof(buf)-1);
    if(r < 0) {
        fprintf(stderr, "can't read link %s: %s\n", LOCALTIME, strerror(errno));
        exit(1);
    }

    buf[r] = '\0';
    char *name = buf;

    char *p1 = strstr(buf, "zone");
    if(p1 != NULL) {
        char *p2;
        p2 = strstr(p1   1, "zone");
        if(p2 != NULL) p1 = p2;
        p2 = strchr(p1, '/');
        if(p2 != NULL)
            name = p2   1;
    }

    printf("%s\n", name);
}

This will work on most Unix-like systems, including Linux and MacOS. It will not work on Windows, which AFAIK does not use the IANA tz database at all. It will not work on systems where /etc/localtime is a file, rather than a symbolic link to one of the zoneinfo files.

If /etc/localtime is a file, there is no good way to determine which zone name it represents. I believe you would have to compare it to all of the files underneath /usr/share/zoneinfo, looking for matching contents.

I'm not sure what magic technique Howard uses in his C solution. (But I mean no disrespect with that word "magic".)

  • Related