web-dev-qa-db-fra.com

`Gmtime ()` rapportera-t-il les secondes à 60 lorsqu'elles sont en une seconde intercalaire?

J'ai un serveur qui tourne dans TZ=UTC et j'ai un code comme celui-ci:

time_t t = time(NULL);
struct tm tm;
gmtime_r(&t, &tm);

La question est Will tm.tm_sec == 60 lorsque le serveur est dans une seconde intercalaire?

Par exemple, si j'étais dans la période suivante:

1998-12-31T23:59:60.00 - 915 148 800.00
1998-12-31T23:59:60.25 - 915 148 800.25
1998-12-31T23:59:60.50 - 915 148 800.50
1998-12-31T23:59:60.75 - 915 148 800.75
1999-01-01T00:00:00.00 - 915 148 800.00

est-ce que gmtime() renverrait tm == 1998-12-31T23:59:60 pour time_t = 915148800 et, une fois hors de la seconde intercalaire, renverrait tm == 1999-01-01T00:00:00 pour le même time_t?

14
Yuki

La réponse courte est non, dans la pratique, gmtime_r ne remplira jamais tm_sec par 60. C'est malheureux, mais inévitable.

Le problème fondamental est que time_t correspond, selon la norme Posix, au nombre de secondes écoulées depuis le 1970-01-01 UTC en supposant qu’il n’existe pas de secondes intercalaires .

Au cours de la dernière seconde sautée, la progression était la suivante:

1483228799    2016-12-31 23:59:59
1483228800    2017-01-01 00:00:00

Oui, il aurait dû y avoir une seconde intercalaire, 23:59:60. Mais il n'y a pas de valeur possible time_t entre 1483228799 et 1483228800.

Je connais deux façons pour une variante gmtime de renvoyer une heure se terminant par :60:

  1. Vous pouvez exécuter l'horloge de votre système d'exploitation sur autre chose que l'UTC, généralement TAI ou TAI-10, et utiliser les fuseaux horaires "corrects" pour convertir en UTC (ou heure locale) l'affichage. Voir cette page Web } _ pour une discussion à ce sujet.

  2. Vous pouvez utiliser clock_gettime() et définir une nouvelle valeur clkid, peut-être CLOCK_UTC, qui contourne le problème time_t en utilisant des valeurs struct timespec délibérément non normalisées lorsque cela est nécessaire. Par exemple, pour obtenir une valeur d'heure comprise entre 1483228799 et 1483228800, vous devez définir tv_sec sur 1483228799 et tv_nsec sur 1000000000. Voir cette page Web } _ pour plus de détails.

La voie n ° 1 fonctionne plutôt bien, mais personne ne l'utilise parce que personne ne veut utiliser son horloge de noyau sur autre chose que l'UTC qu'elle est censée être. (Vous finissez par avoir des problèmes avec des éléments tels que les horodatages de système de fichiers et des programmes tels que tar qui intègrent ces horodatages.)

La voie n ° 2 est une belle idée, OMI, mais à ma connaissance, elle n’a jamais été mise en œuvre dans un système d’exploitation publié. (En l'occurrence, j'ai une implémentation fonctionnelle pour Linux, mais je n'ai pas encore publié mon travail.) Pour que la deuxième méthode fonctionne, vous avez besoin d'une nouvelle variante gmtime, peut-être gmtime_ts_r, qui accepte un struct timespec au lieu d'un time_t.


Addendum: Je viens de relire le titre de votre question. Vous avez demandé: "gmtime() rapportera-t-il 60 secondes lorsque le serveur est sur le pas d'une seconde?" Nous pourrions répondre à cette question en disant "oui, mais", avec l'avertissement suivant: étant donné que la plupart des serveurs ne peuvent pas représenter le temps correctement pendant une seconde relevée, ils jamais "sur" une seconde relevée.


Addendum 2: J'ai oublié de mentionner que le schéma n ° 1 semble mieux fonctionner pour les heures locales - c'est-à-dire lorsque vous appelez l'une des variantes localtime - que pour les heures UTC et gmtime. Il est clair que les conversions effectuées par localtime sont affectées par la valeur de la variable d’environnement TZ, mais il n’est pas aussi clair que TZ ait un effet sur gmtime. J'ai observé que certaines implémentations de gmtime sont influencées par TZ et peuvent donc effectuer des secondes intercalaires conformément aux zones "correctes", et certaines ne le peuvent pas. En particulier, la gmtime dans GNU glibc semble accorder une attention particulière aux informations de seconde avancée dans une zone "de droite" si TZ en spécifie une, alors que gmtime dans le IANA tzcode distribution pas.

11
Steve Summit

La question est Will tm.tm_sec == 60 lorsque le serveur est dans une seconde intercalaire?

Non, sur un système UNIX typique, time_t compte le nombre de non sauts secondes depuis l'époque (1970-01-01 00:00:00 GMT). En tant que tel, convertir un time_t en struct tm donnera toujours une structure temporelle avec une valeur tm_sec comprise entre 0 et 59.

Ignorer les secondes intercalaires dans le calcul time_t permet de convertir un time_t en une date/heure lisible par l'homme, sans connaissance complète de toutes les secondes intercalaires antérieures à cette heure. Il permet également de convertir sans ambiguïté les valeurs time_t dans le futur. l'inclusion de secondes intercalaires rendrait cela impossible, car la présence d'une seconde intercalaire n'est pas connue au-delà de 6 mois dans le futur.

Il existe plusieurs manières pour les systèmes UNIX et de type UNIX de gérer les secondes intercalaires. Plus généralement, soit:

  1. Une valeur time_t est répétée pendant la seconde intercalaire. (Ceci est le résultat d'une interprétation stricte des normes, mais entraînera un dysfonctionnement de nombreuses applications, car il semble que le temps soit passé en arrière.)

  2. La durée du système est légèrement ralentie pendant un certain temps autour de la seconde intercalaire pour «étaler» la seconde intercalaire sur une période plus longue. (Cette solution a été adoptée par de nombreuses grandes plates-formes cloud, y compris Google et Amazon . Elle évite les incohérences d'horloge locale, au détriment de désynchroniser les systèmes affectés d'une demi-seconde avec UTC. pendant toute la durée.)

  3. L'heure système est réglée sur TAI. Étant donné que cela n'inclut pas les secondes intercalaires, aucun traitement des secondes intercalaires n'est nécessaire. (Ceci est rare, car il laissera le système désynchronisé plusieurs secondes par rapport aux systèmes UTC, qui constituent la majeure partie du monde. Toutefois, il peut s’avérer une option viable pour les systèmes peu ou pas en contact avec le monde extérieur, et donc n'ont aucun moyen d'apprendre des secondes intercalaires à venir.)

  4. Le système ignore complètement les secondes intercalaires, mais son client NTP corrige l'horloge après que la seconde intercalaire a laissé l'horloge du système à une seconde de la bonne heure. ( C'est ce que fait Windows. )

5
duskwuff

Il n'y a absolument aucune réponse facile à cela. Pour qu'il y ait 60 secondes lorsqu'il y a une seconde intercalaire, il faut 1) quelque chose dans le système d'exploitation pour savoir qu'il reste une seconde intercalaire, et 2) pour la bibliothèque C que vous utilisez également connaître la seconde intercalaire et faire quelque chose avec elle. 

Beaucoup de systèmes d'exploitation et de bibliothèques ne le font pas. 

Le meilleur que j'ai trouvé est constitué par les versions modernes du noyau Linux associées à gpsd et ntpd, utilisant un récepteur GPS comme référence de temps. Le GPS annonce des secondes intercalaires dans son flux de données système et gpsd, ntpd et le noyau Linux peuvent conserver CLOCK_TAI pendant que la seconde intercalaire se produit et l'horloge système est également correcte. Je ne sais pas si la glibc fait une chose sensée avec la seconde intercalaire. 

Sur d'autres UNIX, votre kilométrage variera. Considérablement. 

Windows est une zone sinistrée de *******. Par exemple, la classe DateTime en C # ne connaît pas les secondes intercalaires historiques. L'horloge système sautera d'une seconde la prochaine fois qu'une mise à jour de l'heure du réseau sera reçue. 

2
bazza

POSIX spécifie la relation entre les valeurs de time_t "secondes depuis l'époque" et le temps décomposé (struct tm) d'une manière qui n'admet pas de secondes intercalaires ni de TAI, donc essentiellement (jusqu'à une certaine ambiguïté quant à ce qui devrait se passer près de secondes intercalaires), Les valeurs POSIX time_t sont UT1, pas UTC, et les résultats de gmtime le reflètent. Il n'y a vraiment aucun moyen d'adapter ou de changer cela, de manière compatible avec les spécifications existantes et les logiciels existants basés sur celles-ci.

La bonne façon d’aller de l’avant est presque certainement un mélange de ce que Google a fait avec le lissage en seconde intercalaire et une formule standardisée pour la conversion aller-retour entre les heures "UTC en barbotage" et "UTC réel" (et donc aussi en temps TAI) dans le journal. Une fenêtre de 24 heures autour d'une seconde intercalaire et des API pour effectuer ces conversions.

2
R..

J'ai lu ceci sur www.cplusplus.com à propos de gmtime: "Utilise la valeur indiquée par timer pour remplir une structure tm avec les valeurs qui représentent l'heure correspondante, exprimée sous forme d'heure UTC (c'est-à-dire l'heure correspondant au fuseau horaire GMT)".

Donc, il y a une contradiction. UTC a des secondes de longueur absolument constante et a donc besoin de secondes intercalaires, tandis que GMT a des jours exactement 86 400 secondes de longueurs très légèrement variables. gmtime () ne peut pas fonctionner en même temps en UTC et en GMT. 

Quand on nous dit que gmtime () renvoie "UTC en supposant qu'il n'y ait pas de secondes intercalaires", je suppose que cela signifie GMT. Cela signifierait qu'il n'y aurait pas de secondes intercalaires enregistrées et que le temps s'écarterait lentement de l'heure UTC, jusqu'à ce que la différence soit d'environ 0,9 seconde et qu'une seconde intercalaire soit ajoutée en heure UTC, mais pas en heure GMT. C'est facile à gérer pour les développeurs mais pas tout à fait précis. 

Une alternative consiste à avoir des secondes constantes jusqu'à ce que vous soyez près d'une seconde intercalaire, puis à ajuster environ 1 000 secondes autour de cette seconde intercalaire. Il est également facile à manipuler, 100% précis la plupart du temps et 0,1% d’erreur en secondes, parfois pendant 1 000 secondes. 

Et la deuxième alternative est d'avoir des secondes constantes, des secondes intercalaires, puis de les oublier. Donc, gmtime () retournera la même seconde deux fois de suite, passant de x secondes 0 nanosecondes à x secondes 999999999 nanosecondes, puis à nouveau de x secondes 0 nanosecondes à x secondes 999999999 nanosecondes, puis à x + 1 secondes. Ce qui va causer des problèmes. 

Bien sûr, il serait utile d’avoir une autre horloge qui renverra le temps UTC exact, y compris les secondes intercalaires, avec des secondes parfaitement précises. Pour traduire "secondes depuis Epoch" en année, mois, jour, heures, minutes, secondes, il faut connaître toutes les secondes intercalaires depuis Epoch (ou avant Epoch si vous gérez des temps antérieurs). Et une horloge qui retournera une heure GMT exacte garantie sans secondes intercalaires et sans seconde presque constante. 

1
gnasher729

Un autre problème par rapport à leur problème est de disposer d’une bibliothèque qui «le sait» sur les secondes intercalaires. La plupart des bibliothèques ne le font pas et, par conséquent, les réponses apportées par des fonctions telles que gmtime sont, à proprement parler, inexactes au cours d'une seconde intercalaire. De plus, les calculs de différence de temps produisent souvent des résultats inexacts chevauchant une seconde. Par exemple, la valeur de time_t qui vous a été attribuée à la même heure UTC hier est exactement 86400 secondes plus petite que la valeur actuelle, même s'il y avait en fait une seconde. 

La communauté astronomique a résolu ce problème. Voici la Bibliothèque SOFA qui contient des routines de temps appropriées. Voir leur manuel (PDF) , la section sur les échelles de temps. Si elles font partie de votre logiciel et sont tenues à jour (une nouvelle version est nécessaire pour chaque nouvelle seconde), vous disposez de calculs, conversions et affichages horaires précis. 

0
bazza