Penso che tu voglia clock_gettime
con CLOCK_TAI
per funzionare correttamente. Anch'io.
La frase critica nella risposta a cui si fa riferimento è:"Si noti che l'offset da CLOCK_REALTIME viene inizializzato all'avvio su zero e né ntpd né chronyd lo impostano per impostazione predefinita sul valore corretto (attualmente 35)."
Questo potrebbe essere ancora vero, a parte il fatto che l'offset ora è 37, ma un recente ntpd può almeno essere configurato per impostare l'offset. Ho fatto quanto segue su una macchina openSUSE:
vi /etc/ntp.conf # Add the line: leapfile /var/lib/ntp/etc/ntp.leapseconds
update-leap
service ntpd restart
less /var/log/ntp # Check for errors
Poi clock_gettime(CLOCK_TAI, &res)
sembrava funzionare correttamente.
Penso che ntp imposti l'offset usando ntp_adjtime
con MOD_TAI
. Ricerca nella fonte chrony con grep -P '(ADJ|MOD)_TAI'
non trova corrispondenze, quindi sembra che chrony non abbia ancora questa capacità.
Puoi usare libtai
da djb:https://cr.yp.to/libtai.html
Che cos'è?
libtai è una libreria per memorizzare e manipolare date e orari.
libtai supporta due scale temporali:(1) TAI64, che copre alcune centinaia di miliardi di anni con una precisione di 1 secondo; (2) TAI64NA, che copre lo stesso periodo con una precisione di 1 attosecondo. Entrambe le scale sono definite in termini di TAI, l'attuale standard internazionale in tempo reale.
libtai fornisce un formato interno per TAI64, struct tai, progettato per manipolazioni rapide. Le routine tai_pack() e tai_unpack() convertono tra struct tai e un formato di archiviazione TAI64 portatile a 8 byte.libtai fornisce formati interni ed esterni simili per TAI64NA.
libtai fornisce struct caldate per memorizzare le date nella forma anno-mese-giorno. Può convertire struct caldate, sotto il calendario gregoriano, in un numero di giorno giuliano modificato per una facile aritmetica delle date.
libtai fornisce struct caltime per memorizzare le date e gli orari del calendario insieme agli offset UTC. Può convertire da struct tai a struct caltime in UTC, tenendo conto dei secondi intercalari, per una visualizzazione accurata di data e ora. Può anche riconvertire da struct caltime a struct tai per l'input dell'utente. La sua velocità complessiva di conversione da UTC a TAI è 100 volte migliore rispetto alla normale implementazione UNIX mktime().
Questa versione di libtai richiede un sistema UNIX con gettimeofday(). Sarà facile eseguire il porting su altri sistemi operativi con compilatori che supportano l'aritmetica a 64 bit.
Il codice sorgente di libtai è di pubblico dominio.
Mentre eseguo chrony
invece del vecchio ntpd
, non avevo un modo automatico per ottenere i parametri del kernel corretti, quindi ho cercato un'alternativa.
Poiché l'offset tra TAI e UTC è relativamente costante (cambia meno di una volta all'anno), è possibile impostare staticamente il parametro del kernel, quindi utilizzare l'orologio CLOCK_TAI in un'applicazione darà il valore corretto.
C'è un'applicazione di prova per impostare l'offset del kernel nei sorgenti del kernel, in tools/testing/selftests/timers/set-tai.c
. E, supponendo che tu abbia il tzdata
pacchetto installato, c'è un file con l'offset tra UTC e TAI in /usr/share/zoneinfo/leap-seconds.list
.
Ho ridotto l'applicazione di test del kernel in modo che il main diventasse:
int main(int argc, char **argv)
{
int i, ret;
ret = get_tai();
printf("tai offset started at %i\n", ret);
if (argc < 2)
{
printf("New offset not given, not setting\n");
}
else
{
i = strtol(argv[1],NULL,10);
printf("Attempting to set TAI offset to %d\n",i);
printf("Checking tai offsets can be properly set: ");
ret = set_tai(i);
ret = get_tai();
if (ret != i) {
printf("[FAILED] expected: %i got %i\n", i, ret);
return EXIT_FAILURE;
}
}
printf("[OK]\n");
return EXIT_SUCCESS;
}
Quindi, per il mio caso d'uso, si trattava solo di estrarre il valore corretto da leap-seconds.list
file ed eseguendo set-tai
con this come parametro (in /etc/rc.local
per farlo accadere al momento dell'avvio). Un modo di esempio per farlo è:
TAI_OFFSET=$(grep -v '^#' /usr/share/zoneinfo/leap-seconds.list | tail -1 | awk '{ print $2 }')
if [ -x /usr/local/sbin/set-tai ]; then
/usr/local/sbin/set-tai $TAI_OFFSET
fi
Spero che questo sia utile a qualcun altro!