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".)