Come posso trovare il fuso orario attuale del sistema?

Su Linux, ho bisogno di trovare il fuso orario attualmente configurato come posizione di Olson. Voglio che il mio codice (C o C ++) sia portabile su quanti più sistemi Linux possibili.

Per esempio. Vivo a Londra, quindi la mia attuale posizione di Olson è “Europa / Londra”. Non mi interessano gli ID di fuso orario come “BST”, “EST” o qualsiasi altra cosa.

Debian e Ubuntu hanno un file /etc/timezone che contiene questa informazione, ma non credo di poter contare sul fatto che il file sia sempre lì, posso? Gnome ha una funzione oobs_time_config_get_timezone() che restituisce anche la stringa corretta, ma voglio che il mio codice funzioni su sistemi senza Gnome.

Quindi, qual è il miglior modo generale per ottenere il fuso orario attualmente configurato come posizione di Olson, su Linux?

È difficile ottenere una risposta affidabile . Affidarsi a cose come /etc/timezone potrebbe essere la soluzione migliore.

(La variabile tzname e il membro tm_zone di struct tm , come suggerito in altre risposte, in genere contengono un’abbreviazione come GMT / BST ecc, piuttosto che la stringa temporale di Olson come richiesto nella domanda).

  • Sui sistemi basati su Debian (inclusa Ubuntu), /etc/timezone è un file che contiene la risposta giusta.
  • Su alcuni sistemi basati su Redhat (incluse almeno alcune versioni di CentOS, RHEL, Fedora), è ansible ottenere le informazioni richieste usando readlink () su /etc/localtime , che è un /etc/localtime simbolico a (ad esempio) /usr/share/zoneinfo/Europe/London .
  • OpenBSD sembra utilizzare lo stesso schema di RedHat.

Tuttavia, ci sono alcuni problemi con gli approcci di cui sopra. La /usr/share/zoneinfo contiene anche file come GMT e GB , quindi è ansible che l’utente possa configurare il link simbolico in modo che punti lì.

Inoltre non c’è nulla che impedisca all’utente di copiare il file timezone corretto lì invece di creare un link simbolico.

Una possibilità per aggirare questo problema (che sembra funzionare su Debian, RedHat e OpenBSD) è confrontare i contenuti del file / etc / localtime nei file in / usr / share / zoneinfo, e vedere quali corrispondono:

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

Naturalmente lo svantaggio è che questo ti dirà tutti i fusi orari identici a quelli attuali. (Ciò significa identico nel senso pieno – non solo “attualmente allo stesso tempo”, ma anche “cambia sempre i loro orologi nello stesso giorno fino a quando il sistema lo sa”).

La tua migliore possibilità potrebbe essere quella di combinare i metodi sopra elencati: usa /etc/timezone se esiste; altrimenti prova ad analizzare /etc/localtime come link simbolico; se fallisce, cerca i file di definizione del fuso orario corrispondenti; se fallisce, rinuncia e torna a casa 😉

(E non ho idea se uno dei precedenti si applica a AIX …)

Non esiste una funzione standard c o c ++ per questo. Tuttavia, GNU libc ha un’estensione. la sua struct tm ha due membri extra:

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

Ciò significa che se si utilizza una delle funzioni che popola una struct tm (come localtime o gmtime ) è ansible utilizzare questi campi aggiuntivi. Questo è ovviamente solo se si utilizza GNU libc (e una versione sufficientemente recente di esso).

Anche molti sistemi hanno un int gettimeofday(struct timeval *tv, struct timezone *tz); funzione (POSIX) che riempie un struct timezone . Questo ha i seguenti campi:

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

Non esattamente quello che hai chiesto, ma chiudi …

Molto tardi, ma stavo cercando qualcosa di simile e ho scoperto che la libreria ICU ha la possibilità di ottenere l’ID del fuso orario di Olson: http://userguide.icu-project.org/datetime/timezone

Ora è installato sulla maggior parte delle distribuzioni Linux (installa il pacchetto libicu-dev o equivalente). Codice:

 #include  #include  using namespace U_ICU_NAMESPACE; int main() { TimeZone* tz = TimeZone::createDefault(); UnicodeString us; tz->getID(us); std::string s; us.toUTF8String(s); std::cout << "Current timezone ID: " << s << '\n'; delete tz; return 0; } 

E per ottenere i nomi dei nomi di fuso orario abbreviati / POSIX (dovrebbe funzionare anche su Windows):

 #include  int main() { time_t ts = 0; struct tm t; char buf[16]; ::localtime_r(&ts, &t); ::strftime(buf, sizeof(buf), "%z", &t); std::cout << "Current timezone (POSIX): " << buf << std::endl; ::strftime(buf, sizeof(buf), "%Z", &t); std::cout << "Current timezone: " << buf << std::endl; 

Vedo due casi principali di Linux:

  1. Ubuntu. Dovrebbe esserci un file / etc / timezone. Questo file dovrebbe contenere solo il fuso orario e nient’altro.
  2. Cappello rosso. Ci dovrebbe essere un / etc / sysconfig / clock che contiene qualcosa come: ZONE = “America / Chicago”

Inoltre, Solaris dovrebbe avere un file / etc / TIMEZONE che contiene una riga come: TZ = US / Mountain

Quindi, sulla base di quanto sopra, qui c’è un certo C che credo risponda alla domanda dell’OP. L’ho provato su Ubuntu, CentOS (Red Hat) e Solaris (bonus).

 #include  #include  #include  char *findDefaultTZ(char *tz, size_t tzSize); char *getValue(char *filename, char *tag, char *value, size_t valueSize); int main(int argc, char **argv) { char tz[128]; if (findDefaultTZ(tz, sizeof(tz))) printf("Default timezone is %s.\n", tz); else printf("Unable to determine default timezone.\n"); return 0; } char *findDefaultTZ(char *tz, size_t tzSize) { char *ret = NULL; /* If there is an /etc/timezone file, then we expect it to contain * nothing except the timezone. */ FILE *fd = fopen("/etc/timezone", "r"); /* Ubuntu. */ if (fd) { char buffer[128]; /* There should only be one line, in this case. */ while (fgets(buffer, sizeof(buffer), fd)) { char *lasts = buffer; /* We don't want a line feed on the end. */ char *tag = strtok_r(lasts, " \t\n", &lasts); /* Idiot check. */ if (tag && strlen(tag) > 0 && tag[0] != '#') { strncpy(tz, tag, tzSize); ret = tz; } } fclose(fd); } else if (getValue("/etc/sysconfig/clock", "ZONE", tz, tzSize)) /* Redhat. */ ret = tz; else if (getValue("/etc/TIMEZONE", "TZ", tz, tzSize)) /* Solaris. */ ret = tz; return ret; } /* Look for tag=someValue within filename. When found, return someValue * in the provided value parameter up to valueSize in length. If someValue * is enclosed in quotes, remove them. */ char *getValue(char *filename, char *tag, char *value, size_t valueSize) { char buffer[128], *lasts; int foundTag = 0; FILE *fd = fopen(filename, "r"); if (fd) { /* Process the file, line by line. */ while (fgets(buffer, sizeof(buffer), fd)) { lasts = buffer; /* Look for lines with tag=value. */ char *token = strtok_r(lasts, "=", &lasts); /* Is this the tag we are looking for? */ if (token && !strcmp(token, tag)) { /* Parse out the value. */ char *zone = strtok_r(lasts, " \t\n", &lasts); /* If everything looks good, copy it to our return var. */ if (zone && strlen(zone) > 0) { int i = 0; int j = 0; char quote = 0x00; /* Rather than just simple copy, remove quotes while we copy. */ for (i = 0; i < strlen(zone) && i < valueSize - 1; i++) { /* Start quote. */ if (quote == 0x00 && zone[i] == '"') quote = zone[i]; /* End quote. */ else if (quote != 0x00 && quote == zone[i]) quote = 0x00; /* Copy bytes. */ else { value[j] = zone[i]; j++; } } value[j] = 0x00; foundTag = 1; } break; } } fclose(fd); } if (foundTag) return value; return NULL; } 

Ho lavorato su una libreria C ++ 11/14 libera e open source che affronta questa domanda in una singola riga di codice:

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

È pensato per essere portabile su tutti i recenti gusti di Linux, macOS e Windows. Per me questo programma emette:

 America/New_York 

Se scarichi questa libreria e non ti funziona, segnalazioni di bug sono benvenute.

Mi è piaciuto il post fatto da psmears e ho implementato questo script per leggere il primo output della lista. Naturalmente ci devono essere modi più eleganti per farlo, ma ci sei …

  /** * Returns the (Linux) server default timezone abbreviation * To be used when no user is logged in (Ex.: batch job) * Tested on Fedora 12 * * @param void * @return String (Timezone abbreviation Ex.: 'America/Sao_Paulo') */ public function getServerTimezone() { $shell = 'md5sum /etc/localtime'; $q = shell_exec($shell); $shell = 'find /usr/share/zoneinfo -type f | xargs md5sum | grep ' . substr($q, 0, strpos($q, '/') - 2); $q = shell_exec($shell); $q = substr($q, strpos($q, 'info/') + 5, strpos($q, " ")); return substr($q, 0, strpos($q, chr(10))); } 

Nella mia Fedora 12 brasiliana, restituisce:
Brasile / Est

E fa esattamente ciò di cui ho bisogno.

Grazie, psm

FWIW, RHEL / Fedora / CentOS hanno /etc/sysconfig/clock :

 ZONE="Europe/Brussels" UTC=true ARC=false 

Ecco il codice che funziona per la maggior parte delle versioni di Linux.

 #include  #include  #include  #include  #include  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; } 

Secondo questa pagina, sembra che tu #include dichiarerà quanto segue.

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

Ti dà le informazioni di cui hai bisogno?

Su Linux, ho bisogno di trovare il fuso orario corrente come posizione di Olson. Voglio che il mio codice (C o C ++) sia portabile su quanti più sistemi Linux possibili.

Se si desidera essere portatili, quindi utilizzare solo GMT internamente. A causa della navigazione multiutente, * l’orologio di sistema NIX è normalmente in GMT e non esiste un fuso orario esteso a livello di sistema, in quanto diversi utenti connessi al sistema potrebbero vivere in fusi orari diversi.

Il fuso orario specifico dell’utente si riflette nella variabile di ambiente TZ e potrebbe essere necessario utilizzarlo solo quando si converte la data / ora interna nel formato leggibile dall’utente. Altrimenti, localtime() prenderà automaticamente cura di te.

La libc accede al database Olson quando viene chiamato tzset e utilizza in seguito i fusi orari semplificati. tzset guarda prima la variabile d’ambiente TZ e torna ad analizzare i dati binari in /etc/localtime .

Inizialmente systemd standardizzato con il nome del fuso orario Olson in /etc/timezone , in stile Debian . Dopo systemd 190 e / usr merge, systemd legge e aggiorna solo /etc/localtime , con l’ulteriore requisito che il file sia un link simbolico a /usr/share/zoneinfo/${OLSON_NAME} .

Guardando TZ , quindi readlink("/etc/localtime") , è il modo più affidabile per abbinare la logica tzset di libc e mantenere comunque i nomi Olson simbolici. Per i sistemi che non seguono la convenzione symf di sistema, leggere /etc/timezone (e possibilmente controllare che /usr/share/zoneinfo/$( sia lo stesso di /etc/localtime ) è un buon ripiego .

Se riesci a vivere senza nomi simbolici, l' analisi del /etc/localtime tzfile /etc/localtime è tanto più portatile quanto più, anche se molto più complesso. Leggendo solo l'ultimo campo si ottiene un fuso orario Posix (ad esempio: CST5CDT,M3.2.0/0,M11.1.0/1 ), che può interagire con alcune librerie di gestione del tempo, ma elimina alcuni dei metadati (senza cronologia informazioni sulla transizione).

Dato che tzselect non è stato menzionato da nessuno e hai bisogno di una soluzione a prova di goof, lavora con quello che ha fatto Olson. Ottieni i file tzcode e tzdata da elsie, oltre ai file di tabulazione.

ftp://elsie.nci.nih.gov

A marzo 2017, la corretta posizione da cui scaricare sarà ftp://ftp.iana.org/tz/releases (e scarica tzcode2017a.tar.gz e tzdata2017a.tar.gz ).

Quindi scarica tzselect.ksh dal download di glibc. Quindi puoi vedere come decodificare i fusi orari. Un punto critico: a volte dovrai chiedere in quale paese e città si trova la casella di linux. Puoi serializzare quei dati se vuoi, e poi verificarlo rispetto ai dati del fuso orario che puoi trovare.

Non c’è modo di farlo in modo affidabile tutto il tempo senza la possibilità di intervento da parte dell’utente, ad esempio, come parte dell’installazione del programma.

Buona fortuna per l’Arizona in generale, e nell’Indiana occidentale … speriamo che il tuo codice funzioni altrove.