Obtener el desplazamiento GMT de la zona horaria en C

6 minutos de lectura

estoy usando el estándar mktime función para convertir un struct tm en un valor de tiempo de época. los tm los campos se completan localmente y necesito obtener la hora de la época como GMT. tm tiene un gmtoff campo para permitirle establecer el desplazamiento GMT local en segundos solo para este propósito.

Pero no puedo entender cómo obtener Esa información. ¿Seguramente debe haber una función estándar en algún lugar que devuelva el desplazamiento? Cómo localtime ¿hazlo?

  • tm tiene un gmtoff field” –> que es una extensión de biblioteca C no estándar.

    – chux – Reincorporar a Monica

    1 de febrero de 2018 a las 21:36

avatar de usuario
alca

Solo haz lo siguiente:

#define _GNU_SOURCE /* for tm_gmtoff and tm_zone */

#include <stdio.h>
#include <time.h>

/* Checking errors returned by system calls was omitted for the sake of readability. */
int main(void)
{
  time_t t = time(NULL);
  struct tm lt = {0};

  localtime_r(&t, &lt);

  printf("Offset to GMT is %lds.\n", lt.tm_gmtoff);
  printf("The time zone is '%s'.\n", lt.tm_zone);

  return 0;
}

Nota: Los segundos desde la época devueltos por time() se miden como en Greenwich.

  • Realmente me gusta esta solución simple, pero encontré un problema con su código. creo struct tm = {0}; debiera ser struct tm result = {0}; y luego puedes usar result en lugar de tm en el resto del código. +1 de mi parte si pudieras cambiar eso.

    – Serrano

    3 de septiembre de 2013 a las 15:23

  • También me gustaría ver el código para Windows. ¡Cualquier ayuda apreciada!

    – Pantalón Bhupesh

    17 de julio de 2014 a las 8:58

  • tm_gmtoff, tm_zone son extensiones no C Std.

    – chux – Reincorporar a Monica

    1 de febrero de 2018 a las 21:33

avatar de usuario
iabdalkader

¿Cómo lo hace localtime?

De acuerdo a localtime página man

La función localtime() actúa como si llamara a tzset(3) y establece las variables externas tzname con información sobre la zona horaria actual,
zona horaria con la diferencia entre la hora universal coordinada (UTC) y la hora estándar local en segundos

Así que podrías llamar localtime() y tendrás la diferencia en timezone o llamar tzset():

extern long timezone;
....
tzset();
printf("%ld\n", timezone);

Nota: si elige ir con localtime_r() tenga en cuenta que no es necesario establecer esas variables que deberá llamar tzset() primero en establecer timezone:

De acuerdo con POSIX.1-2004, se requiere que localtime() se comporte como si se llamara a tzset(), mientras que localtime_r() no tiene este requisito. Para el código portátil, tzset() debe llamarse antes que localtime_r()

Supongo que debería haber buscado un poco más antes de preguntar. Resulta que hay un poco conocido timegm función que hace lo contrario de gmtime. Es compatible con GNU y BSD, lo cual es lo suficientemente bueno para mis propósitos. Una solución más portátil es establecer temporalmente el valor de la TZ variable de entorno a “UTC” y luego usar mktimeluego establezca TZ espalda.

Pero timegm funciona para mi.

  • El enlace dice “Estas funciones son extensiones GNU no estándar que también están presentes en los BSD. Evite su uso”.

    – satur9nine

    26/04/2016 a las 18:51

avatar de usuario
colina

La versión universal de obtener la función de compensación de hora local está aquí.
Tomé prestados fragmentos de código de esta respuesta en stackoverflow.

int time_offset()
{
    time_t gmt, rawtime = time(NULL);
    struct tm *ptm;

#if !defined(WIN32)
    struct tm gbuf;
    ptm = gmtime_r(&rawtime, &gbuf);
#else
    ptm = gmtime(&rawtime);
#endif
    // Request that mktime() looksup dst in timezone database
    ptm->tm_isdst = -1;
    gmt = mktime(ptm);

    return (int)difftime(rawtime, gmt);
}

avatar de usuario
Mariusz Jaskółka

Esta es la solución portátil que debería funcionar en todas las plataformas C (y C++) estándar:

const std::time_t epoch_plus_11h = 60 * 60 * 11;
const int local_time = localtime(&epoch_plus_11h)->tm_hour;
const int gm_time = gmtime(&epoch_plus_11h)->tm_hour;
const int tz_diff = local_time - gm_time;

Agregar std:: espacio de nombres cuando se usa C++. El resultado está en horas en el rango [-11, 12];

Explicación:
Simplemente convertimos la fecha y hora “1970-01-01 11:00:00” a tm estructura dos veces: con la zona horaria local y con el GMT. El resultado es la diferencia entre las horas parciales.

Se ha elegido “11:00::00” porque este es el único punto de tiempo (considerando GMT) cuando tenemos la misma fecha en todo el mundo. Por ese hecho, no tenemos que considerar la magia adicional con el cambio de fecha en los cálculos.

ADVERTENCIA

La versión anterior de mi respuesta funcionó solo en Linux:

// DO NOT DO THAT!!
int timezonez_diff = localtime(&epoch_plus_11h)->tm_hour -
                     gmtime(&epoch_plus_11h)->tm_hour;

Es posible que esto no funcione porque el almacenamiento para el resultado tm objeto devuelto como un puntero de localtime y gmtime se puede compartir (y está en windows/msvc). Ahí es donde he introducido temporales para el cálculo.

  • Bonito y sencillo. Pero no todas las zonas horarias están en número entero de horas. Lo mejor es calcular los minuetos también.

    – Tb.

    19 de febrero de 2021 a las 18:52

  • ¿Cómo maneja eso el horario de verano?

    – bbonev

    14 de enero a las 2:22

Creo que lo siguiente es cierto al menos en Linux: la información de la zona horaria proviene de /usr/share/zoneinfo/. localtime lee /etc/localtime que debería ser una copia del archivo apropiado de zoneinfo. Puedes ver lo que hay dentro haciendo zdump -v en el archivo de zona horaria (zdump puede estar en sbin pero no necesita permisos elevados para leer archivos de zona horaria con él). Aquí hay un recorte de uno:

/usr/share/zoneinfo/EST5EDT  Sun Nov  6 05:59:59 2033 UTC = Sun Nov  6 01:59:59 2033 EDT isdst=1 gmtoff=-14400
/usr/share/zoneinfo/EST5EDT  Sun Nov  6 06:00:00 2033 UTC = Sun Nov  6 01:00:00 2033 EST isdst=0 gmtoff=-18000
/usr/share/zoneinfo/EST5EDT  Sun Mar 12 06:59:59 2034 UTC = Sun Mar 12 01:59:59 2034 EST isdst=0 gmtoff=-18000
/usr/share/zoneinfo/EST5EDT  Sun Mar 12 07:00:00 2034 UTC = Sun Mar 12 03:00:00 2034 EDT isdst=1 gmtoff=-14400
/usr/share/zoneinfo/EST5EDT  Sun Nov  5 05:59:59 2034 UTC = Sun Nov  5 01:59:59 2034 EDT 

Supongo que podrías analizar esto tú mismo si quieres. No estoy seguro de si hay una función stdlib que simplemente devuelve el gmtoff (puede que la haya, pero no sé…)

editar: man tzfile describe el formato del archivo zoneinfo. Debería poder simplemente mapear en una estructura del tipo apropiado. Parece ser lo que está haciendo zdump basado en un rastro de él.

  • Bonito y sencillo. Pero no todas las zonas horarias están en número entero de horas. Lo mejor es calcular los minuetos también.

    – Tb.

    19 de febrero de 2021 a las 18:52

  • ¿Cómo maneja eso el horario de verano?

    – bbonev

    14 de enero a las 2:22

avatar de usuario
Duke Nukem

Aquí hay una frase de dos líneas inspirada en las respuestas de @Hill y @friedo:

#include <time.h>
...
time_t rawtime = time(0);
timeofs = timegm(localtime(&rawtime)) - rawtime;

Devuelve el desplazamiento de UTC en segundos.

no necesita _GNU_SOURCE definido, pero tenga en cuenta que timegm no es un estándar POSIX y es posible que no esté disponible fuera de GNU y BSD.

¿Ha sido útil esta solución?

Esta web utiliza cookies propias y de terceros para su correcto funcionamiento y para fines analíticos y para mostrarte publicidad relacionada con sus preferencias en base a un perfil elaborado a partir de tus hábitos de navegación. Al hacer clic en el botón Aceptar, acepta el uso de estas tecnologías y el procesamiento de tus datos para estos propósitos. Configurar y más información
Privacidad