================================================================================
DOCUMENTATION TECHNIQUE - NTPClient (fork local)
================================================================================
Fichier      : __DOC_NTPClient.txt
Version doc  : 2.0
Date         : 2025
================================================================================

================================================================================
1. PRESENTATION
================================================================================

NTPClient est une librairie Arduino permettant la synchronisation de l'heure
via le protocole NTP (Network Time Protocol) sur une connexion UDP/IP.

  Lib d'origine : NTPClient v3.2.1 par Fabrice Weinberg (2015)
  Depot GitHub  : https://github.com/arduino-libraries/NTPClient
  Licence       : MIT

Ce fork local est base sur la v3.2.1 officielle et integre deux series de
corrections documentees sous les references FIX v1.0 et FIX v1.1.

La version est fixee a 99.0.0 dans library.properties pour bloquer toute
tentative de mise a jour automatique par l'IDE Arduino (voir section 3).

================================================================================
2. PRINCIPE DE FONCTIONNEMENT NTP
================================================================================

Le protocole NTP utilise UDP sur le port 123. L'Arduino envoie une requete de
48 bytes a un serveur de temps (pool.ntp.org par defaut) et recoit en retour
un paquet de 48 bytes contenant le timestamp courant.

  FLUX RESEAU :

  [Arduino / ESP]                    [pool.ntp.org : port 123]
       |                                          |
       |--- requete UDP 48 bytes ---------------> |
       |                                          |
       |<-- reponse UDP 48 bytes ----------------- |
       |    bytes[40..43] = Transmit Timestamp    |
       |    (secondes depuis 01/01/1900 00:00:00) |

  CONVERSION TIMESTAMP :
    epoch Unix = Transmit Timestamp - 2208988800
                                      ^^^^^^^^^
                                      SEVENZYYEARS = 70 ans en secondes
                                      (ecart entre 1900 et 1970)

  CALCUL HEURE AFFICHEE :
    heure = _currentEpoc + _timeOffset + (millis() - _lastUpdate) / 1000
            ^^^^^^^^^^^^^^^^^^^^^^^^^^   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
            valeur NTP convertie          derive depuis la derniere synchro

  L'Arduino ne reste pas connecte en permanence au serveur NTP.
  Apres chaque synchro, il compte le temps ecoule via millis().

--------------------------------------------------------------------------------
  STRUCTURE DU PAQUET NTP (48 bytes)
--------------------------------------------------------------------------------

  Offset  Taille  Champ
  ------  ------  -------------------------------------------------
    0       1     LI (2 bits) | Version (3 bits) | Mode (3 bits)
                    LI      : Leap Indicator (11 = non synchronise)
                    Version : doit etre >= 4
                    Mode    : 3=Client (requete), 4=Server (reponse)
    1       1     Stratum (1..15 = valide, 0=unspecified, 16=unsync)
    2       1     Poll Interval
    3       1     Precision
    4       4     Root Delay
    8       4     Root Dispersion
   12       4     Reference Identifier
   16       8     Reference Timestamp     <-- verifie par isValid()
   24       8     Originate Timestamp
   32       8     Receive Timestamp
   40       8     Transmit Timestamp      <-- UTILISE pour l'heure
                    bytes[40..41] = partie haute (highWord)
                    bytes[42..43] = partie basse (lowWord)
                    secsSince1900 = highWord << 16 | lowWord

================================================================================
3. PROBLEME DE MISE A JOUR AUTOMATIQUE ARDUINO IDE
================================================================================

  MECANISME :
  L'IDE Arduino interroge au demarrage le registre officiel :
    https://downloads.arduino.cc/libraries/library_index.json
  Si le champ "name" de library.properties correspond a une lib connue
  ET que "version" est inferieure a la derniere release, l'IDE propose
  une mise a jour qui ecraserait tous les fichiers du dossier.

  SOLUTION APPLIQUEE :
    version=99.0.0  dans library.properties
    → L'IDE croit avoir une version superieure a toute release officielle
    → Aucune notification de mise a jour n'est affichee
    → Les fichiers FIX v1.0/v1.1 sont preserves

  DOSSIER D'INSTALLATION :
    Windows : C:\Users\<nom>\Documents\Arduino\libraries\NTPClient-3.2.1\
    Linux   : ~/Arduino/libraries/NTPClient-3.2.1/
    Mac     : ~/Documents/Arduino/libraries/NTPClient-3.2.1/

================================================================================
4. HISTORIQUE DES VERSIONS
================================================================================

--------------------------------------------------------------------------------
  VERSION ORIGINE : NTPClient 3.2.1 (Fabrice Weinberg - officielle)
--------------------------------------------------------------------------------

  Fonctions presentes :
    + NTPClient(UDP&)
    + NTPClient(UDP&, int timeOffset)
    + NTPClient(UDP&, const char* server)
    + NTPClient(UDP&, const char* server, int timeOffset)
    + NTPClient(UDP&, const char* server, int timeOffset, unsigned long interval)
    + begin() / begin(int port)
    + update()          → true si a jour, true si pas de maj necessaire
    + forceUpdate()     → sans flush prealable, sans validation paquet
    + end()
    + getEpochTime()
    + getFormattedTime(unsigned long secs = 0)
    + getFormattedDate(unsigned long secs = 0)   PRESENT dans ancienne lib
    + getDay() / getHours() / getMinutes() / getSeconds()
    + setTimeOffset(int)
    + setUpdateInterval(unsigned long)
    + setEpochTime(unsigned long)                PRESENT dans ancienne lib
    + isValid(byte*)    PRESENT dans ancienne lib (avec bug byte[22][22])
    + macro LEAP_YEAR(Y)                         PRESENTE dans ancienne lib

  BUG CONNU v3.2.1 :
    isValid() : ntpPacket[22] compare deux fois (au lieu de [22] et [23])
    → byte[23] du ReferenceTimestamp jamais verifie

--------------------------------------------------------------------------------
  VERSION OFFICIELLE POSTERIEURE (upstream apres 3.2.1)
  [Reference pour comprendre ce qui a ete supprime]
--------------------------------------------------------------------------------

  Changements upstream (non appliques dans ce fork) :
    - getFormattedDate()  SUPPRIME
    - setEpochTime()      SUPPRIME
    - LEAP_YEAR macro     SUPPRIMEE
    - isValid()           SUPPRIME (remplace par flush avant envoi)
    + IPAddress support   ajoute (constructeurs supplementaires)
    + isTimeSet()         ajoute
    + setPoolServerName() ajoute
    + setRandomPort()     ajoute
    + const sur getters   ajoute
    ~ _timeOffset         int → long
    ~ _port               int → unsigned int
    ~ update()            return false si pas de maj (cassure API)
    ~ getFormattedTime()  sans parametre secs (regression)

  MEMOIRE upstream : 75 bytes/instance (+6 bytes vs FIX)

--------------------------------------------------------------------------------
  FIX v1.0 - Base de ce fork
--------------------------------------------------------------------------------

  Objectif : recuperer les fonctions supprimees par upstream tout en
             integrant la meilleure strategie de validation NTP disponible.

  REINTEGRE :
    + getFormattedDate(unsigned long secs = 0)
        Calcule la date JJ/MM/AAAA depuis un timestamp epoch.
        Gere les annees bissextiles via macro LEAP_YEAR.
        Fonctionne en UTC (ne tient pas compte de _timeOffset).

    + setEpochTime(unsigned long secs)
        Injecte manuellement un timestamp dans _currentEpoc.
        Utile pour restauration depuis RTC (DS1307, DS3231) apres reset.

    + macro LEAP_YEAR(Y)
        Requise par getFormattedDate().
        LEAP_YEAR(Y) = (Y>0) && !(Y%4) && ((Y%100) || !(Y%400))

    + isValid(byte*) de la lib upstream (plus robuste que l'ancienne)
        Conserve pour valider le contenu du paquet NTP recu.

  SUPPRIME :
    - Identifiants nom/version upstream dans library.properties
      → prevent auto-update ecrasement par Arduino IDE

  NON REINTEGRE (volontaire) :
    - isValid() de l'ancienne lib 3.2.1
      → remplace par la version upstream qui est plus complete,
        mais avec bug [22][22] non encore corrige (corrige en FIX v1.1)

  CONSERVE (vs upstream) :
    ~ update() : return true si pas de maj necessaire (API stable)
    ~ getFormattedTime(secs=0) : parametre conserve (flexibilite)
    ~ _timeOffset en int (memoire optimisee pour AVR)
    ~ pas de IPAddress (evite +4 bytes RAM inutile sur cibles sans IP directe)

  MEMOIRE FIX v1.0 : 69 bytes/instance

--------------------------------------------------------------------------------
  FIX v1.1 - Corrections de stabilite et ajouts utilitaires
--------------------------------------------------------------------------------

  BUG CORRIGE :
    isValid() byte[22] compare deux fois → corrige en [22] et [23]
    Ligne originale : ntpPacket[22] == 0 && ntpPacket[22] == 0
    Ligne corrigee  : ntpPacket[22] == 0 && ntpPacket[23] == 0
    Impact : le 8eme byte du ReferenceTimestamp n'etait jamais verifie,
             permettant a un paquet partiellement nul de passer la validation.

  AJOUTE :
    + Flush UDP avant sendNTPPacket() dans forceUpdate()
        while(this->_udp->parsePacket() != 0) this->_udp->flush();
        Vide la file UDP avant d'envoyer la requete pour garantir que
        la reponse recue correspond bien a la requete courante, et non
        a un paquet residuel d'une requete precedente.

    + isTimeSet() const
        Retourne true si _lastUpdate != 0.
        Permet de verifier avant d'afficher l'heure qu'une synchro NTP
        a bien eu lieu au moins une fois depuis le demarrage.
        Usage : if(ntp.isTimeSet()) { Serial.println(ntp.getFormattedTime()); }

    + setPoolServerName(const char* poolServerName)
        Modifie _poolServerName a la volee sans reconstruire l'objet.
        Utile pour basculer sur un serveur de secours si forceUpdate() echoue.
        Usage : ntp.setPoolServerName("fr.pool.ntp.org");

  CONSERVE (inchange vs FIX v1.0) :
    ~ Toutes les fonctions FIX v1.0
    ~ Semantique update() return true si pas de maj
    ~ getFormattedTime(secs=0) avec parametre
    ~ Empreinte memoire : 69 bytes/instance (inchangee)

================================================================================
5. DESCRIPTION DETAILLEE DES FONCTIONS
================================================================================

--------------------------------------------------------------------------------
  CONSTRUCTEURS
--------------------------------------------------------------------------------

  NTPClient(UDP& udp)
    Entree  : reference vers objet UDP (WiFiUDP, EthernetUDP...)
    Sortie  : objet NTPClient initialise avec valeurs par defaut
    Defauts : server="pool.ntp.org", port=1337, offset=0, interval=60000ms

  NTPClient(UDP& udp, int timeOffset)
    Entree  : udp + decalage horaire en secondes
    Exemple : 3600 pour UTC+1 (France hiver), 7200 pour UTC+2 (France ete)

  NTPClient(UDP& udp, const char* poolServerName)
    Entree  : udp + nom du serveur NTP
    Exemple : "fr.pool.ntp.org", "time.google.com"

  NTPClient(UDP& udp, const char* poolServerName, int timeOffset)
    Entree  : udp + serveur + offset

  NTPClient(UDP& udp, const char* poolServerName, int timeOffset,
            unsigned long updateInterval)
    Entree  : udp + serveur + offset + intervalle en millisecondes
    Exemple : 30000 = synchro toutes les 30 secondes

--------------------------------------------------------------------------------
  begin() / begin(int port)
--------------------------------------------------------------------------------

  Entree  : port UDP local optionnel (defaut : 1337)
  Sortie  : aucune
  Action  : initialise le socket UDP local sur le port specifie
            positionne _udpSetup = true
  Note    : appele automatiquement par update() si pas encore fait

    +------------------+
    |  _udp->begin()   |
    |  _port = port    |
    |  _udpSetup = true|
    +------------------+

--------------------------------------------------------------------------------
  update()
--------------------------------------------------------------------------------

  Entree  : aucune
  Sortie  : bool - true si heure valide (synchro OK ou pas necessaire)
                   false si synchro echouee (timeout)
  Action  : verifie si l'intervalle _updateInterval est ecoule ou si
            c'est la premiere synchro (_lastUpdate == 0), et appelle
            forceUpdate() si necessaire.

  MACHINE D'ETAT :

    [APPEL update()]
          |
    _lastUpdate == 0 ?  OUI ──────────────────────────+
          |                                            |
         NON                                           |
          |                                            |
    millis() - _lastUpdate >= _updateInterval ?        |
         OUI ──────────────────────────────────────────+
          |                                            |
         NON                                           v
          |                               _udpSetup ? NON → begin()
    return true                                        |
    (pas de maj necessaire)                           OUI
                                                       |
                                               forceUpdate()
                                                       |
                                           return true / false

--------------------------------------------------------------------------------
  forceUpdate()
--------------------------------------------------------------------------------

  Entree  : aucune
  Sortie  : bool - true si synchro reussie, false si timeout (>1000ms)
  Variables modifiees : _lastUpdate, _currentEpoc, _packetBuffer

  ORGANIGRAMME :

    +-------------------------------------+
    | Flush file UDP existante            |  FIX v1.1
    | while(parsePacket) udp->flush()     |
    +-------------------------------------+
                    |
    +-------------------------------------+
    | sendNTPPacket()                     |
    | envoi requete 48 bytes port 123     |
    +-------------------------------------+
                    |
    +-------------------------------------+  <---------+
    | delay(10ms)                         |            |
    | cb = parsePacket()                  |            |
    | timeout++                           |            |
    +-------------------------------------+            |
                    |                                  |
           cb == 0 ?                                   |
           OUI ──> timeout > 100 ? OUI → return false  |
                        NON ──────────────────────────-+
                   |
           cb > 0
                   |
    +-------------------------------------+
    | udp->read(_packetBuffer, 48)        |
    +-------------------------------------+
                   |
    +-------------------------------------+
    | isValid(_packetBuffer) ?            |
    | NON → cb = 0 ──────────────────────-+  (reboucle)
    | OUI ↓                               |
    +-------------------------------------+
                   |
    _lastUpdate = millis() - (10 * (timeout+1))
    highWord = packetBuffer[40..41]
    lowWord  = packetBuffer[42..43]
    secsSince1900 = highWord << 16 | lowWord
    _currentEpoc  = secsSince1900 - SEVENZYYEARS
    return true

--------------------------------------------------------------------------------
  isValid(byte* ntpPacket)
--------------------------------------------------------------------------------

  Entree  : pointeur vers buffer paquet NTP 48 bytes
  Sortie  : bool - true si paquet conforme au protocole NTP
  Variables modifiees : aucune (lecture seule)

  ORGANIGRAMME DE VALIDATION :

    [Byte 0 bits 7-6] LI == 0b11 ?
      OUI → return false  (serveur non synchronise)
      NON ↓

    [Byte 0 bits 5-3] Version >> 3 < 4 ?
      OUI → return false  (version NTP trop ancienne)
      NON ↓

    [Byte 0 bits 2-0] Mode != 0b100 ?
      OUI → return false  (pas une reponse serveur)
      NON ↓

    [Byte 1] Stratum < 1 ou > 15 ?
      OUI → return false  (stratum invalide)
      NON ↓

    [Bytes 16-23] ReferenceTimestamp tous nuls ?   ← FIX v1.1 : [22][23]
      OUI → return false  (serveur pas encore synchro)
      NON ↓

    return true

  DETAIL BYTE[0] REQUETE vs REPONSE :
    Requete  envoyee : 0b11100011  LI=11, V=4, Mode=3 (client)
    Reponse attendue : LI != 11, Version >= 4, Mode == 4 (server)

--------------------------------------------------------------------------------
  isTimeSet() const                                              [FIX v1.1]
--------------------------------------------------------------------------------

  Entree  : aucune
  Sortie  : bool - true si _lastUpdate != 0

  Logique : _lastUpdate est mis a jour par forceUpdate() a chaque synchro
            reussie. Il vaut 0 a l'initialisation (jamais synchro).

  Usage typique :
    if (!ntp.isTimeSet()) {
      Serial.println("En attente de synchro NTP...");
    } else {
      Serial.println(ntp.getFormattedTime());
    }

--------------------------------------------------------------------------------
  getEpochTime()
--------------------------------------------------------------------------------

  Entree  : aucune
  Sortie  : unsigned long - secondes depuis 01/01/1970 00:00:00 UTC

  Calcul :
    epoch = _timeOffset + _currentEpoc + (millis() - _lastUpdate) / 1000
            ^^^^^^^^^^^^   ^^^^^^^^^^^^   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
            fuseau horaire  derniere NTP   temps ecoule depuis synchro (drift)

  Note : getDay(), getHours(), getMinutes(), getSeconds() appellent tous
         getEpochTime() et appliquent les divisions/modulos correspondants.

--------------------------------------------------------------------------------
  getFormattedTime(unsigned long secs = 0)
--------------------------------------------------------------------------------

  Entree  : secs - timestamp epoch a formater (0 = heure courante)
  Sortie  : String au format "HH:MM:SS"

  Calcul :
    rawTime = secs ? secs : getEpochTime()
    hours   = (rawTime % 86400) / 3600
    minutes = (rawTime % 3600) / 60
    seconds =  rawTime % 60

  Note : accepte un timestamp arbitraire grace au parametre secs.
         Contrairement a la version upstream qui a supprime ce parametre.

--------------------------------------------------------------------------------
  getFormattedDate(unsigned long secs = 0)                       [FIX v1.0]
--------------------------------------------------------------------------------

  Entree  : secs - timestamp epoch a formater (0 = date courante)
  Sortie  : String au format "JJ/MM/AAAA"

  ATTENTION : utilise l'heure UTC, ne tient pas compte de _timeOffset.
              Pour la date locale, passer getEpochTime() + _timeOffset.

  ALGORITHME :

    rawTime = epoch / 86400  (nombre de jours depuis 01/01/1970)

    Etape 1 - Trouver l'annee :
    +------------------------------------------+
    | year = 1970                               |
    | TANT QUE days += (bissextile ? 366 : 365) |
    |         <= rawTime                        |
    |   year++                                 |
    +------------------------------------------+
    rawTime -= jours des annees precedentes

    Etape 2 - Trouver le mois :
    +------------------------------------------+
    | monthDays = {31,28,31,30,31,30,           |
    |              31,31,30,31,30,31}           |
    | POUR chaque mois (0..11)                  |
    |   si fevrier → 29 si bissextile, 28 sinon |
    |   SI rawTime < monthLength → break        |
    |   rawTime -= monthLength                  |
    +------------------------------------------+

    Etape 3 - Formater :
    month++ (janvier = 1)
    rawTime++ (jour = 1-base)
    return "JJ/MM/AAAA"

  MACRO LEAP_YEAR(Y) :
    (Y>0) && !(Y%4) && ((Y%100) || !(Y%400))
    → vrai si Y divisible par 4, sauf siecles non divisibles par 400

--------------------------------------------------------------------------------
  setEpochTime(unsigned long secs)                               [FIX v1.0]
--------------------------------------------------------------------------------

  Entree  : secs - timestamp Unix (secondes depuis 01/01/1970)
  Sortie  : aucune
  Variable modifiee : _currentEpoc

  Utilisation typique avec RTC :
    DateTime now = rtc.now();
    ntp.setEpochTime(now.unixtime());
    // L'heure NTP est maintenant initialisee depuis la RTC
    // sans attendre une synchro reseau

  Note : _lastUpdate n'est PAS mis a jour par setEpochTime().
         isTimeSet() retournera false jusqu'a un forceUpdate() reussi.
         Pour simuler une synchro complete, appeler aussi :
         ntp.setEpochTime(secs);  // inject epoch
         // puis laisser update() faire la synchro NTP au prochain cycle

--------------------------------------------------------------------------------
  setTimeOffset(int timeOffset)
--------------------------------------------------------------------------------

  Entree  : offset en secondes (positif ou negatif)
  Sortie  : aucune
  Exemples: 3600  → UTC+1  (France heure hiver)
            7200  → UTC+2  (France heure ete)
            -18000 → UTC-5 (EST USA)

--------------------------------------------------------------------------------
  setUpdateInterval(unsigned long updateInterval)
--------------------------------------------------------------------------------

  Entree  : intervalle en millisecondes
  Sortie  : aucune
  Defaut  : 60000 ms (60 secondes)
  Conseil : ne pas descendre sous 15000 ms pour respecter les serveurs NTP

--------------------------------------------------------------------------------
  setPoolServerName(const char* poolServerName)                  [FIX v1.1]
--------------------------------------------------------------------------------

  Entree  : chaine C de nom de serveur NTP
  Sortie  : aucune
  Exemples: "fr.pool.ntp.org"
            "time.google.com"
            "time.cloudflare.com"
            "0.pool.ntp.org"

  Utile pour basculer sur un serveur de secours :
    if (!ntp.forceUpdate()) {
      ntp.setPoolServerName("time.google.com");
      ntp.forceUpdate();
    }

--------------------------------------------------------------------------------
  sendNTPPacket()  [private]
--------------------------------------------------------------------------------

  Entree  : aucune
  Sortie  : aucune
  Action  : construit et envoie un paquet NTP de 48 bytes au serveur

  STRUCTURE DU PAQUET EMIS :
    _packetBuffer[0]  = 0b11100011  LI=11, Version=4, Mode=3(client)
    _packetBuffer[1]  = 0           Stratum unspecified
    _packetBuffer[2]  = 6           Poll interval
    _packetBuffer[3]  = 0xEC        Precision (-20)
    _packetBuffer[4..11]  = 0       Root Delay + Root Dispersion
    _packetBuffer[12] = 0x49 'I'
    _packetBuffer[13] = 0x4E 'N'    Reference Identifier = "INIT"
    _packetBuffer[14] = 0x49 'I'
    _packetBuffer[15] = 0x52 'R'
    _packetBuffer[16..47] = 0       Timestamps (non renseignes en mode client)

  Envoi UDP : udp->beginPacket(server, 123)
              udp->write(buffer, 48)
              udp->endPacket()

================================================================================
6. EMPREINTE MEMOIRE
================================================================================

  MEMOIRE STATIQUE PAR INSTANCE (compilation AVR - Uno/Nano/Mega)
  ---------------------------------------------------------------

  Membre               Type              Taille AVR
  -------------------  ----------------  ----------
  _udp                 UDP* (ptr)         2 bytes
  _udpSetup            bool               1 byte
  _poolServerName      const char* (ptr)  2 bytes
  _port                int                2 bytes
  _timeOffset          int                2 bytes
  _updateInterval      unsigned long      4 bytes
  _currentEpoc         unsigned long      4 bytes
  _lastUpdate          unsigned long      4 bytes
  _packetBuffer[48]    byte[]            48 bytes
                                        ----------
  TOTAL                                 69 bytes / instance

  Comparaison avec version upstream posterieure a 3.2.1 :
    Upstream : 75 bytes (+ IPAddress 4 bytes + long vs int 2 bytes)
    FIX v1.1 : 69 bytes → economie de 6 bytes par instance

  MEMOIRE FLASH (code programme) - ordre de grandeur :
    getFormattedDate() : ~300 bytes de code supplementaire
    isValid()          : ~80  bytes de code supplementaire
    isTimeSet()        : ~10  bytes de code supplementaire

================================================================================
7. TABLEAU RECAPITULATIF DES FONCTIONS PAR VERSION
================================================================================

  Fonction                    v3.2.1    Upstream post  FIX v1.0  FIX v1.1
  --------------------------  --------  -------------  --------  --------
  begin()                       OUI         OUI          OUI       OUI
  begin(port)                   OUI         OUI          OUI       OUI
  update()                      OUI         OUI*         OUI       OUI
  forceUpdate()                 OUI         OUI**        OUI       OUI***
  end()                         OUI         OUI          OUI       OUI
  getEpochTime()                OUI         OUI          OUI       OUI
  getFormattedTime(secs=0)      OUI         NON****      OUI       OUI
  getFormattedDate(secs=0)      OUI         NON          OUI       OUI
  getDay/Hours/Minutes/Seconds  OUI         OUI          OUI       OUI
  setTimeOffset()               OUI         OUI          OUI       OUI
  setUpdateInterval()           OUI         OUI          OUI       OUI
  setEpochTime()                OUI         NON          OUI       OUI
  isValid() [private]           OUI[bug]    NON          OUI[bug]  OUI[OK]
  isTimeSet()                   NON         OUI          NON       OUI
  setPoolServerName()           NON         OUI          NON       OUI
  setRandomPort()               NON         OUI          NON       NON
  IPAddress constructeurs       NON         OUI          NON       NON
  LEAP_YEAR macro               OUI         NON          OUI       OUI
  Flush avant envoi             NON         OUI          NON       OUI
  Getters const                 NON         OUI          NON       NON

  *    upstream update() retourne false si pas de maj (cassure API)
  **   upstream forceUpdate() sans validation paquet isValid()
  ***  FIX v1.1 : flush prealable + isValid() corrige
  **** upstream getFormattedTime() sans parametre secs (regression)

================================================================================
FIN DU DOCUMENT
================================================================================
