web-dev-qa-db-fra.com

Obtenir le décalage horaire GMT du fuseau horaire en C

J'utilise la fonction standard mktime pour transformer un struct tm en valeur temporelle Epoch. Les champs tm sont remplis localement et je dois obtenir l'heure Epoch au format GMT. tm a un champ gmtoff pour vous permettre de définir le décalage GMT local en secondes à cette fin. 

Mais je ne peux pas comprendre comment obtenir cette information. Il doit sûrement y avoir une fonction standard quelque part qui retournera le décalage. Comment localtime le fait-il?

15
friedo

Je suppose que j'aurais dû faire un peu plus de recherches avant de demander. Il s’avère qu’il existe une fonction timegm peu connue qui fait l’opposé de gmtime. Il est supporté par GNU et BSD, ce qui convient assez à mes besoins. Une solution plus portable consiste à définir temporairement la valeur de la variable d'environnement TZ sur "UTC", à utiliser mktime, puis à redéfinir TZ.

Mais timegm fonctionne pour moi.

6
friedo

Il suffit de faire ce qui suit:

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

Remarque: Les secondes depuis Epoch renvoyé par time() sont mesurées comme si elles étaient à Greenwich.

20
alk

Comment localtime le fait-il?

Selon la page de manuel localtime

La fonction localtime () agit comme si elle appelait tzset (3) et définissait le paramètre variables externes tzname avec des informations sur le fuseau horaire actuel, timezone avec la différence entre Coordinated Universal Heure (UTC) et heure normale locale en secondes

Donc, vous pouvez soit appeler localtime() et vous aurez la différence dans timezone ou appeler tzset():

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

Remarque: si vous choisissez d'utiliser localtime_r(), notez qu'il n'est pas nécessaire de définir ces variables, vous devrez d'abord appeler tzset() pour définir timezone:

Selon POSIX.1-2004, localtime () doit se comporter comme si tzset () a été appelé, alors que localtime_r () ne l’a pas exigence. Pour le code portable, tzset () doit être appelé avant localtime_r ()

6
iabdalkader

La version universelle de l'obtention de la fonction de décalage horaire local est ici.
J'ai emprunté des morceaux de code à la réponse dans statckoverflow.

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);
}
3
Hill

Je pense que ce qui suit est vrai au moins sous Linux: les informations sur le fuseau horaire proviennent de/usr/share/zoneinfo /. localtime lit/etc/localtime qui devrait être une copie du fichier approprié de zoneinfo. Vous pouvez voir ce qu'il y a à l'intérieur en faisant zdump -v sur le fichier de fuseau horaire (zdump peut être dans sbin mais vous n'avez pas besoin d'autorisations élevées pour lire les fichiers de fuseau horaire avec elle). En voici un extrait:

/usr/share/zoneinfo/EST5EDT Dim 6 Nov 05 05:59:59 2033 UTC = Dim Nov 6 01:59:59 2033 EDT isdst = 1 gmtoff = -14400 
/usr/share/zoneinfo/EST5EDT Dim Nov 6 06:00:00 2033 UTC = Dim Nov 6 01:00:00 2033 EST isdst = 0 gmtoff = -18000 
/Usr/share/zoneinfo/EST5EDT Dim. Mars 12 06:59:59 2034 UTC = Dim 12 mars 01:59:59 2034 EST isdst = 0 gmtoff = -18000 
/Usr/share/zoneinfo/EST5EDT dim. Mars 12 07:00:00 2034 UTC = dim. Mars 12 03:00:00 20:53 HAE isdst = 1 gmtoff = -14400 
/Usr/share/zoneinfo/EST5EDT dim. 5 nov. 05:59:59 2034 UTC = dim. 5 nov. 01:59:59 2034 EDT 

Je suppose que vous pourriez analyser cela vous-même si vous voulez. Je ne suis pas sûr s'il existe une fonction stdlib qui retourne simplement le gmtoff (il y en a peut-être mais je ne sais pas ...)

modifier: man tzfile décrit le format du fichier zoneinfo. Vous devriez pouvoir mapper simplement dans une structure du type approprié. Il semble que ce soit ce que zdump est en train de faire.

0
frankc