Obtenir l'heure locale à partir du temps GMT

Bonjour à toutes et à tous,

J'ai un module GPS qui me donne le temps GMT sous la forme JJ/MM/AAAA HH:MM:SS. Cette valeur est issue des phrases NMEA.

J'aimerais convertir cette valeur en heure locale en lui ajoutant le nombre d'heures de décalage correspondant au lieu où je me trouve.

Y a-t-il quelque chose de spécifique sous Arduino.

Merci pour votre aide.

Pierre

Bonjour

Par exemple ceci, bricolé vite fait à partir de ma lib simpleRTC :

//Passage de l'heure UTC à l'heure Locale
void convertirLocal(uint8_t *annee, uint8_t *mois, uint8_t *jour, uint8_t *heure) //Attention année sur deux chiffres uniquement
{
  uint8_t decalage = periodeEte(*annne, *mois, *jour, *heure) ? 2 : 1;
  for (uint8_t i = 0; i < decalage; i++)
  {
    ajouter1Heure(annee, mois, jour, heure);
  }
}

bool periodeEte(uint8_t anneeUTC, uint8_t moisUTC, uint8_t jourUTC, uint8_t heureUTC)
{
  //En France métropolitaine :
  //Passage de l'heure d'hiver à l'heure d'été le dernier dimanche de mars à 1h00 UTC (à 2h00 locales il est 3h00)
  //Passage de l'heure d'été à l'heure d'hiver le dernier dimanche d'octobre à 1h00 UTC (à 3h00 locales il est 2h00)
  const uint8_t MARS = 3;
  const uint8_t OCTOBRE = 10;
  if (moisUTC == MARS)
  {
    uint8_t dernierDimancheMars = 31 - ((5 + anneeUTC + (anneeUTC >> 2)) % 7); //Pas évidente à trouver celle-là
    return jourUTC > dernierDimancheMars || (jourUTC == dernierDimancheMars && heureUTC != 0);
  }
  if (moisUTC == OCTOBRE)
  {
    uint8_t dernierDimancheOctobre = 31 - ((2 + anneeUTC + (anneeUTC >> 2)) % 7);
    return jourUTC < dernierDimancheOctobre || (jourUTC == dernierDimancheOctobre && heureUTC == 0);
  }
  return MARS < moisUTC && moisUTC < OCTOBRE;
}

void ajouter1Heure(uint8_t *annee, uint8_t *mois, uint8_t *jour, uint8_t *heure)
{
  //Un peu long, mais simple et efficace
  if (*heure < 23)
  {
    (*heure)++;
  }
  else
  {
    *heure = 0;
    if (*jour < nbJoursMois(*annee, *mois))
    {
      (*jour)++;
    }
    else
    {
      *jour = 1;
      if (*mois < 12)
      {
        (*mois)++;
      }
      else
      {
        *mois = 1;
        (*annee)++;
      }
    }
  }
}

uint8_t nbJoursMois(uint8_t annee, uint8_t mois)
{
  return (mois==4 || mois==6 || mois==9 || mois==11) ? 30 : (mois==2 ? ((annee&3) ? 28 : 29) : 31);
}

Oui bien sûr la méthode universelle de calcul sur des date/heure est de passer par un genre de timestamp.
Et pour les conversions d'heure locale, toujours dans une approche universelle permettant de gérer tous les fuseaux horaires de par le Monde, il convient d'adopter un timestamp dont l'unité est le quart d'heure (ou moins).

Personnellement, dans le contexte de l'arduino et de l'heure locale de France Métropolitaine (également celle d'un bon nombre de pays d'Europe), je trouve dommage de passer par un calcul de timestamp, décalage, suivi d'une décomposition.
C'est quand même faire abstraction de la proximité des deux horaires, en bouffant beaucoup de cpu pour pas grand chose.
La fonction "ajouter1Heure()" ci-dessus fait le job aussi bien dans ce contexte, avec quelques instructions basiques vite exécutées par le nono : statistiquement, on s'arrête au premier "if " la plupart du temps.

Et tant pis si mon code n'est pas utilisable au Népal :slight_smile:

Par contre, il me semble effectivement que le signal GPS donne l'écart avec l'heure locale, ce qui peut éviter d'avoir à se taper le calcul du dernier dimanche de mars ou d'octobre.

Je m'interroge juste sur la précision du franchissement de frontière, quand on change de fuseau horaire. Cet écart doit être basé sur une espèce de cartographie gps des territoires. Et si je comprends bien le fonctionnement du système GPS, cela suppose que cette cartographie est intégrée dans chaque récepteur?

Merci à tous pour ces réponses. Je vais les décortiquer.

En attendant, mon module GPS, ou pour le moins, la bibliothèque TinyGPS ne me fournit pas la phrase $GPZDPA.

Cordialement.

Pierre

J'ai regardé la méthode ajouterHeure() qui allait dans le sens de ce que je voulait faire, Je l'ai modifiée quelque peu pour qu'on puisse entrer un décalage différent de une heure et que ce décalage puisse être positif ou négatif. Voici la modification :

void changerHeure(uint8_t *annee, uint8_t *mois, uint8_t *jour, int8_t *heure, int8_t decal) {
  if (decal > 0) {
    if (*heure < (24 - decal))
      (*heure) += decal;
    else {
      *heure = (*heure + decal) % 24;
      if (*jour < nbJoursMois(*annee, *mois))
        (*jour)++;
      else {
        *jour = 1;
        if (*mois < 12)
          (*mois)++;
        else {
          *mois = 1;
          (*annee)++;
        }
      }
    }
  }
  else if (decal < 0) {
    if (*heure + decal >= 0)
      (*heure) += decal;
    else {
      *heure = (*heure + decal + 24);
      if (*jour == 1) {
        (*mois)--;
        if (*mois == 0) {
          (*mois) = 12;
          (*annee)--;
        }
        *jour = nbJoursMois(*annee, *mois);
      } else {
        (*jour)--;
      }
    }
  }
}

J'avais essayé de faire ce genre de méthode, mais j'avais l'impression que j'allais m'embêter avec les valeurs négatives. Du coup, j'ai écrit deux méthodes : l'une transformant un DateTime en TempsPOSIX et l'autre, l'inverse. Les voici, mais c'est vari que c'est lourding :

#define SEC_JOUR 86400
int nbJ1J[] = {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334};
byte nbJM[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};

unsigned long nbSec1970(String DT) { // DT sous la forme "JJ/MM/AAAA HH:MM:SS"
  int nbJours = (DT.substring(6, 10).toInt() - 1970) * 365 + (DT.substring(6, 10).toInt() - 1970) / 4;
  nbJours += nbJ1J[DT.substring(3, 5).toInt() - 1];
  if (DT.substring(6, 10).toInt() % 4 == 0 && DT.substring(3, 5).toInt() > 2) nbJours ++;
  nbJours += DT.substring(0, 2).toInt() - 1;
  return nbJours * SEC_JOUR + DT.substring(11, 13).toInt() * 3600 + DT.substring(14, 16).toInt() * 60 + DT.substring(17).toInt();
}

String dateHeure(unsigned long TU) {
  char dh[21];
  unsigned long TUprec = 0xFFFFFFFF;
  int a = 1970;
  while (TU <= TUprec) {
    TUprec = TU;
    if (a % 4 == 0)
      TU -= 366 * SEC_JOUR;
    else
      TU -= 365 * SEC_JOUR;
    a++;
  }
  a--;
  if (a % 4 == 0)
    nbJM[1] = 29;
  else
    nbJM[1] = 28;
  long Tr = TUprec;
  byte m;
  for (m = 0; m < 12; m++) {
    if (Tr - (nbJM[m]) * SEC_JOUR < 0)
      break;
    Tr -= nbJM[m] * SEC_JOUR;
  }
  byte j = Tr / SEC_JOUR;
  Tr = Tr % SEC_JOUR;
  byte h = Tr / 3600;
  Tr = Tr % 3600;
  byte mn = Tr / 60;
  byte s = Tr % 60;
  String formatAff = F("%02d/%02d/%04d %02d:%02d:%02d"); // format de date pour l'affichage (mis en mémoire flash)
  sprintf(dh, formatAff.c_str(), j + 1, m + 1, a, h, mn, s);
  return dh;
}

Une petite question, dans la procédure originale, comment interprète-t-on la ligne suivante :

(annee&3) ? 28 : 29

Je me doute bien que c'est pour tenir compte des années bissextiles, mais je ne comprends pas la syntaxe "annee&3".

Cordialement.

Pierre

Bonjour,

C'est pour voir si l'année est divisible par 4.
Si l'année est divisible par 4 les deux bits de poids faible sont à 0 donc annee&3 vaut 0 (& est le et binaire).
Si la valeur est différente de 0 on prend la première valeur, sinon la deuxième.

if aurait plus clair de mettre

annee%4 ? 28 : 29

Remarque: c'est faux pour les siècles, par exemple 2100 n'est pas bissextile. Tu me diras que d'ici 2100 on a le temps :slight_smile:

kamill:
... (& est le et binaire). ...

Bon sang, mais c'est bien sûr. Où avais-je la tête. :astonished:

Cordialement.

Pierre

J'ai modifié le code que j'avais donné (réponse #6) dans la méthode "changerHeure()" pour la partie heure négative.

Désolé pour l'erreur.

Cordialement.

Pierre