c++ - tienen - zona horaria utc
¿Cómo encuentro la zona horaria actual del sistema? (12)
En Linux, necesito encontrar la zona horaria actualmente configurada como una ubicación de Olson. Quiero que mi código (C o C ++) sea portátil a tantos sistemas Linux como sea posible.
Por ejemplo. Vivo en Londres, por lo que mi ubicación actual en Olson es "Europa / Londres". No estoy interesado en las ID de zona horaria como "BST", "EST" o lo que sea.
Debian y Ubuntu tienen un archivo /etc/timezone
que contiene esta información, pero no creo que pueda confiar en que ese archivo siempre esté allí, ¿puedo? Gnome tiene una función oobs_time_config_get_timezone()
que también devuelve la cadena correcta, pero quiero que mi código funcione en sistemas sin Gnome.
Entonces, ¿cuál es la mejor manera general de obtener la zona horaria actualmente configurada como una ubicación de Olson, en Linux?
En Linux, necesito encontrar la zona horaria actual como una ubicación de Olson. Quiero que mi código (C o C ++) sea portátil a tantos sistemas Linux como sea posible.
Si desea ser portátil, use solo GMT internamente. Debido a la herencia multiusuario, * el reloj del sistema NIX normalmente está en GMT y no hay una zona horaria amplia del sistema, ya que los diferentes usuarios conectados al sistema pueden estar viviendo en diferentes zonas horarias.
La zona horaria específica del usuario se refleja en la variable de entorno TZ
y es posible que deba usarla solo al convertir la fecha / hora interna en el formato legible por el usuario. De lo contrario, localtime()
se encarga de ello automáticamente por usted.
Aquí está el código que funciona para la mayoría de las versiones de 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;
}
Dado que tzselect no fue mencionado por nadie y sí necesita una solución a prueba de tontos, trabaje con lo que hizo Olson. Obtenga los archivos tzcode y tzdata de elsie, más los archivos de pestañas.
En marzo de 2017, la ubicación correcta para descargar sería ftp://ftp.iana.org/tz/releases (y descarga tzcode2017a.tar.gz
y tzdata2017a.tar.gz
).
A continuación, obtenga tzselect.ksh de la descarga de glibc. Entonces puedes ver cómo invertir las zonas horarias. Un punto difícil: a veces, tendrá que preguntar en qué país y ciudad se encuentra la caja de Linux. Si lo desea, puede serializar esos datos y luego verificarlos con los datos de la zona horaria que puede encontrar.
No hay manera de hacer esto de manera confiable todo el tiempo sin la posibilidad de la intervención del usuario, por ejemplo, como parte de la instalación del programa.
Buena suerte en Arizona en general, y en el oeste de Indiana ... espero que su código se ejecute en otra parte.
El libc accede a la base de datos Olson cuando se llama a tzset , y utiliza zonas horarias simplificadas posteriormente. tzset
mira primero la variable de entorno TZ
y vuelve a analizar los datos binarios en /etc/localtime
.
Al principio, systemd estandarizó al tener el nombre de la zona horaria de Olson en /etc/timezone
, Debian-style . Después de systemd 190 y / usr merge, systemd solo lee y actualiza /etc/localtime
, con el requisito adicional de que el archivo sea un enlace simbólico a /usr/share/zoneinfo/${OLSON_NAME}
.
Mirar TZ
, luego readlink("/etc/localtime")
, es la manera más confiable de hacer coincidir la lógica tzset
de libc y mantener los nombres simbólicos de Olson. Para los sistemas que no siguen la convención de enlaces simbólicos de systemd, leer /etc/timezone
(y posiblemente verificar que /usr/share/zoneinfo/$(</etc/timezone)
es lo mismo que /etc/localtime
) es una buena alternativa. .
Si puede vivir sin nombres simbólicos, parsing el /etc/localtime
tzfile es tan portátil como puede ser, aunque mucho más complejo. Al leer solo el último campo se obtiene una zona horaria de Posix (por ejemplo: CST5CDT,M3.2.0/0,M11.1.0/1
), que puede interoperar con algunas bibliotecas de manejo de tiempo, pero elimina algunos de los metadatos (sin historial). información de transición).
Es difícil obtener una respuesta confiable . Confiar en cosas como /etc/timezone
puede ser la mejor apuesta.
(La variable tzname
y el miembro tm_zone
de struct tm
, como se sugiere en otras respuestas, generalmente contienen una abreviatura como GMT
/ BST
, etc., en lugar de la cadena de tiempo de Olson como se solicita en la pregunta).
- En los sistemas basados en Debian (incluido Ubuntu),
/etc/timezone
es un archivo que contiene la respuesta correcta. - En algunos sistemas basados en Redhat (incluidas al menos algunas versiones de CentOS, RHEL, Fedora), puede obtener la información requerida usando readlink () en
/etc/localtime
, que es un enlace simbólico a (por ejemplo)/usr/share/zoneinfo/Europe/London
. - OpenBSD parece usar el mismo esquema que RedHat.
Sin embargo, hay algunos problemas con los enfoques anteriores. El directorio /usr/share/zoneinfo
también contiene archivos como GMT
y GB
, por lo que es posible que el usuario configure el enlace simbólico para que apunte allí.
Además, no hay nada que impida que el usuario copie el archivo de zona horaria correcto en lugar de crear un enlace simbólico.
Una posibilidad para solucionar esto (que parece funcionar en Debian, RedHat y OpenBSD) es comparar el contenido del archivo / etc / localtime con los archivos en / usr / share / zoneinfo, y ver cuáles coinciden:
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
...
...
Por supuesto, la desventaja es que esto le dirá todas las zonas horarias que son idénticas a la actual. (Eso significa que es idéntico en el sentido completo, no solo "actualmente al mismo tiempo", sino también "siempre cambie sus relojes el mismo día en la medida en que el sistema lo sepa".)
Su mejor apuesta puede ser combinar los métodos anteriores: use /etc/timezone
si existe; de lo contrario, intente analizar /etc/localtime
como un enlace simbólico; si eso falla, busque los archivos de definición de zona horaria coincidentes; Si eso falla, abandona y vete a casa ;-)
(Y no tengo idea si alguno de los anteriores se aplica en AIX ...)
FWIW, RHEL / Fedora / CentOS tienen /etc/sysconfig/clock
:
ZONE="Europe/Brussels"
UTC=true
ARC=false
He estado trabajando en una biblioteca gratuita de código abierto C ++ 11/14 que aborda esta pregunta en una sola línea de código:
std::cout << date::current_zone()->name() << ''/n'';
Está diseñado para ser portátil en todas las versiones recientes de Linux, macOS y Windows. Para mi este programa produce:
America/New_York
Si download esta biblioteca y no funciona, los informes de errores son bienvenidos.
Me gustó la publicación realizada por psmears e implementé este script para leer el primer resultado de la lista. Por supuesto, debe haber formas más elegantes de hacer esto, pero ahí están ...
/**
* 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)));
}
En mi brasileña Fedora 12, devuelve:
Brasil / este
Y hace exactamente lo que necesito.
Gracias psmears
Muy tarde en el día, pero estaba buscando algo similar y descubrí que la biblioteca de la UCI tiene la disposición para obtener el ID de la zona horaria de Olson: http://userguide.icu-project.org/datetime/timezone
Ahora está instalado en la mayoría de las distribuciones de linux (instale el paquete libicu-dev o equivalente). Código:
#include <unicode/timezone.h>
#include <iostream>
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;
}
Y para obtener los nombres abreviados de la zona horaria / POSIX (también debería funcionar en Windows):
#include <time.h>
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;
No hay una función c o c ++ estándar para esto. Sin embargo, GNU libc tiene una extensión. Su struct tm
tiene dos miembros adicionales:
long tm_gmtoff; /* Seconds east of UTC */
const char *tm_zone; /* Timezone abbreviation */
Esto significa que si usa una de las funciones que llena una struct tm
(como gmtime
o gmtime
) puede usar estos campos adicionales. Esto es, por supuesto, solo si está utilizando GNU libc (y una versión lo suficientemente reciente).
También muchos sistemas tienen un int gettimeofday(struct timeval *tv, struct timezone *tz);
función (POSIX) que completará una struct timezone
. Esto tiene los siguientes campos:
struct timezone {
int tz_minuteswest; /* minutes west of Greenwich */
int tz_dsttime; /* type of DST correction */
};
No exactamente lo que pediste, pero cerca ...
Según this página, parece que si #include <time.h>
declarará lo siguiente.
void tzset (void);
extern char *tzname[2];
extern long timezone;
extern int daylight;
¿Eso te da la información que necesitas?
Veo dos casos principales de linux:
- Ubuntu. Debe haber un archivo / etc / timezone. Este archivo solo debe contener la zona horaria y nada más.
- Sombrero rojo. Debe haber un / etc / sysconfig / clock que contenga algo como: ZONE = "America / Chicago"
Además, Solaris debe tener un archivo / etc / TIMEZONE que contenga una línea como: TZ = US / Mountain
Entonces, basado en lo anterior, aquí hay algunas C directas que creo que responden a la pregunta del OP. Lo he probado en Ubuntu, CentOS (Red Hat) y Solaris (bonificación).
#include <string.h>
#include <strings.h>
#include <stdio.h>
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;
}