Secondes intercalaires (leap seconds)#

Pourquoi#

Un serveur Stratum 1 ne se contente pas de suivre une horloge qui avance régulièrement : il doit aussi savoir qu’une fois tous les quelques années, une seconde supplémentaire s’insère dans le calendrier civil. Ignorer ce mécanisme, ou le gérer avec un fichier de données périmé, produit un décalage d’une seconde entière, une éternité à l’échelle de précision visée par ce projet (autour de la microseconde). Ce chapitre explique d’où viennent les secondes intercalaires et comment ce projet automatise leur prise en compte de façon robuste.

D’où viennent les secondes intercalaires#

Trois échelles de temps coexistent, avec des règles de progression différentes :

ÉchelleBaseComportement
TAI (Temps Atomique International)Moyenne pondérée de plus de 400 horloges atomiques au césium, réparties dans ~80 laboratoires nationauxMonotone, strictement croissante, jamais de saut
UT1 (Temps Universel Astronomique)Rotation réelle de la Terre, mesurée par interférométrie sur des quasarsRalentit progressivement (effet de marée lunaire), avec des variations irrégulières
UTC (Temps Universel Coordonné)Échelle civile légaleReste proche d’UT1 (écart < 0,9 s) tout en bénéficiant de la stabilité du TAI

La Terre tourne un peu moins vite qu’au moment où la seconde a été définie, un écart minime, mais qui s’accumule. Pour que UTC (l’heure légale que tout le monde utilise) ne dérive jamais de plus de 0,9 seconde par rapport à la rotation réelle de la Terre, l’IERS (International Earth Rotation and Reference Systems Service) insère périodiquement une seconde intercalaire, toujours le 30 juin ou le 31 décembre à 23:59:59 UTC, quand elle est nécessaire.

Depuis la dernière seconde intercalaire (1er janvier 2017), l’écart TAI − UTC = 37 secondes. Cette valeur ne bouge que par paliers d’une seconde entière, annoncés à l’avance par l’IERS via le “Bulletin C”.

Décision majeure pour l’avenir (résolution 4 de la CGPM, 2022) : à partir de 2035, la tolérance UTC-UT1 sera assouplie et les secondes intercalaires seront gelées à leur valeur, un changement de paradigme international après plus de 50 ans de leap seconds, motivé par les difficultés que ces sauts imprévisibles posent aux systèmes informatiques modernes. Voir la résolution 4 de la CGPM.

Comment le GNSS et chrony gèrent cet écart#

Le temps GPS (GPS Time) est, comme le TAI, continu : il ne subit jamais de seconde intercalaire. Actuellement, GPS Time = UTC + 18 secondes. Le récepteur GNSS reçoit la correction UTC courante directement dans le message de navigation diffusé par les satellites (sous-trame 4 du message GPS), rafraîchie plusieurs heures avant chaque échéance potentielle.

Côté serveur, chrony a besoin de sa propre source d’information sur cet écart, via un fichier leap-seconds.list publié par l’IANA/IERS :

# Dans chrony.conf
leapseclist /etc/chrony/leap-seconds.list

Ce fichier contient, entre autres, une date d’expiration, au-delà de laquelle chrony ne peut plus garantir que la correction est à jour, et le signale.

Mise à jour automatisée et validée#

cat > /usr/local/sbin/leap-seconds-update <<'EOF'
#!/bin/bash
# Mise à jour VALIDÉE de /etc/chrony/leap-seconds.list.
# Installation + restart chrony UNIQUEMENT si le contenu a réellement changé.
set -euo pipefail

URL="https://data.iana.org/time-zones/data/leap-seconds.list"
DST="/etc/chrony/leap-seconds.list"
NTP_EPOCH_OFFSET=2208988800   # 1900-01-01 -> 1970-01-01

log()  { logger -t leap-seconds "$1"; }
fail() { log "ERREUR : $1"; exit 1; }

TMP=$(mktemp /tmp/leap-seconds.XXXXXX)
trap 'rm -f "$TMP"' EXIT

curl -fsSL --max-time 60 "$URL" -o "$TMP" || fail "téléchargement impossible"

# a. Validation structurelle (présence des lignes clés)
grep -q '^#\$' "$TMP" || fail "ligne #\$ (dernière MAJ) absente"
grep -q '^#@'  "$TMP" || fail "ligne #@ (expiration) absente"
grep -q '^#h'  "$TMP" || fail "ligne #h (hash) absente"

# b. Le fichier téléchargé n'est pas lui-même déjà expiré
EXPIRY_NTP=$(awk '/^#@/ {print $2; exit}' "$TMP")
EXPIRY_UNIX=$((EXPIRY_NTP - NTP_EPOCH_OFFSET))
[ "$EXPIRY_UNIX" -gt "$(date +%s)" ] || fail "fichier téléchargé déjà expiré"

# c. Intégrité : hash SHA-1 embarqué recalculé et comparé
STORED=$(awk '/^#h/ { for (i=2; i<=NF; i++) printf "%08s", $i; exit }' "$TMP" | tr ' ' '0')
COMPUTED=$( {
    awk '/^#\$/ {printf "%s", $2; exit}' "$TMP"
    awk '/^#@/  {printf "%s", $2; exit}' "$TMP"
    sed 's/#.*$//' "$TMP" | awk 'NF >= 2 {printf "%s%s", $1, $2}'
} | sha1sum | cut -d' ' -f1 )
[ "$STORED" = "$COMPUTED" ] || fail "hash SHA-1 invalide"

# Installation seulement si le contenu a changé
if cmp -s "$TMP" "$DST"; then
    log "aucun changement (expire le $(date -u -d "@$EXPIRY_UNIX" +%F))"
    exit 0
fi

install -o root -g root -m 0644 "$TMP" "$DST"
log "nouveau fichier installé, restart chrony"
systemctl restart chrony   # leapseclist n'est lu qu'au démarrage
EOF
chmod +x /usr/local/sbin/leap-seconds-update

cat > /etc/cron.d/leap-seconds <<'EOF'
17 3 1 * * root /usr/local/sbin/leap-seconds-update
EOF

Le décalage 17 3 (3h17, plutôt qu’une heure ronde comme minuit ou 3h00) évite de faire coïncider cette tâche avec d’autres jobs cron planifiés sur des horaires ronds, une convention courante pour répartir la charge plutôt qu’une valeur arbitraire. Le premier jour du mois (1) donne une fréquence mensuelle, largement suffisante face à un fichier qui change au mieux deux fois par an.

Erreurs à éviter#

Un simple curl sans validation peut écraser un fichier valide par une page d’erreur. Si le serveur distant est temporairement indisponible ou renvoie une page d’erreur CDN, un script naïf (curl -o /etc/chrony/leap-seconds.list ...) écrase silencieusement un fichier fonctionnel par du contenu invalide, chrony échouera alors à interpréter le fichier au prochain redémarrage, potentiellement bien après l’incident de téléchargement, rendant le diagnostic difficile. Toujours télécharger dans un fichier temporaire, valider avant d’installer, et ne remplacer le fichier en place qu’après validation complète.

  • Redémarrer chrony à chaque exécution du cron, même sans changement. Le fichier leap-seconds.list change au mieux deux fois par an, souvent moins. Un systemctl restart chrony inconditionnel à chaque exécution périodique casse inutilement la boucle de discipline d’horloge en cours (cf. chapitre Chrony), ne redémarrer que lorsque le contenu a effectivement changé (cmp -s) est la bonne pratique.
  • Exécuter la mise à jour trop rarement. Une fréquence de mise à jour de “deux fois par an, en espérant que ça tombe juste” signifie qu’un échec de téléchargement ponctuel (panne réseau temporaire) n’est retenté que six mois plus tard, largement après l’expiration potentielle du fichier. Une fréquence mensuelle avec détection d’absence de changement (donc pas de coût en cas d’échec de téléchargement retenté) est un bien meilleur compromis.
  • Oublier que leapseclist n’est lu qu’au démarrage de chronyd. Contrairement au rechargement de certificat TLS (chapitre NGINX/NTS), qui peut se faire à chaud via SIGHUP, un nouveau fichier leap-seconds.list nécessite un redémarrage complet du démon pour être pris en compte, d’où l’utilité de ne le faire que lorsque c’est vraiment nécessaire (point précédent).

Vérifier#

chronyc tracking | grep "Leap status"
# Doit retourner "Normal" en dehors des 24h précédant une seconde intercalaire

# Date d'expiration du fichier actuellement chargé
awk '/^#@/ {print $2}' /etc/chrony/leap-seconds.list

# Test manuel du script (doit logger "aucun changement" si déjà à jour)
/usr/local/sbin/leap-seconds-update
journalctl -t leap-seconds -n 5

📚 Pour aller plus loin

  • Les règles régissant UTC et les secondes intercalaires sont définies par l’UIT (Union Internationale des Télécommunications), recommandation ITU-R TF.460-6.
  • Les annonces officielles de secondes intercalaires sont publiées par l’IERS via son Bulletin C, disponible sur iers.org.
  • La décision de geler les secondes intercalaires à partir de 2035 a été adoptée par la Conférence Générale des Poids et Mesures (CGPM), résolution 4 de 2022, un changement historique après plus de 50 ans de pratique.
  • Le format et le calcul de la correction GPS Time → UTC (paramètres ΔtLS) sont spécifiés dans IS-GPS-200, sous-trame 4, page 18 du message de navigation.