Comment décomposer le temps unix en C

Cela semble quelque chose que personne ne devrait jamais avoir à faire, mais je travaille sur un module de kernel pour un système embarqué (OpenWRT) dans lequel il semble que time.h inclut les types timespec et time_t , ainsi que les fonctions clock_gettime et gmtime , mais n’inclut pas localtime , ctime , time ou, de manière critique, le type tm .

Lorsque je tente de convertir le pointeur de retour de gmtime vers ma propre structure, un segfault est généré.

Je suppose donc que je me contenterais de résoudre le problème de deux manières: il serait intéressant de savoir comment accéder à ce type manquant ou, alternativement, d’utiliser ma propre méthode pour décomposer un horodatage Unix.

Cela devrait être précis (remplit une imitation réduite d’une struct tm , mon year utilise Common Era au lieu d’une époque de 1900):

 struct xtm { unsigned int year, mon, day, hour, min, sec; }; #define YEAR_TO_DAYS(y) ((y)*365 + (y)/4 - (y)/100 + (y)/400) void untime(unsigned long unixtime, struct xtm *tm) { /* First take out the hour/minutes/seconds - this part is easy. */ tm->sec = unixtime % 60; unixtime /= 60; tm->min = unixtime % 60; unixtime /= 60; tm->hour = unixtime % 24; unixtime /= 24; /* unixtime is now days since 01/01/1970 UTC * Rebaseline to the Common Era */ unixtime += 719499; /* Roll forward looking for the year. This could be done more efficiently * but this will do. We have to start at 1969 because the year we calculate here * runs from March - so January and February 1970 will come out as 1969 here. */ for (tm->year = 1969; unixtime > YEAR_TO_DAYS(tm->year + 1) + 30; tm->year++) ; /* OK we have our "year", so subtract off the days accounted for by full years. */ unixtime -= YEAR_TO_DAYS(tm->year); /* unixtime is now number of days we are into the year (remembering that March 1 * is the first day of the "year" still). */ /* Roll forward looking for the month. 1 = March through to 12 = February. */ for (tm->mon = 1; tm->mon < 12 && unixtime > 367*(tm->mon+1)/12; tm->mon++) ; /* Subtract off the days accounted for by full months */ unixtime -= 367*tm->mon/12; /* unixtime is now number of days we are into the month */ /* Adjust the month/year so that 1 = January, and years start where we * usually expect them to. */ tm->mon += 2; if (tm->mon > 12) { tm->mon -= 12; tm->year++; } tm->day = unixtime; } 

Mes excuses pour tous les nombres magiques. 367 * month / 12 est une astuce intéressante pour générer la séquence de 30/31 jours du calendrier. Le calcul fonctionne avec les années qui commencent en mars jusqu’à la fin, ce qui facilite les choses, car le jour bissextile tombe à la fin d’une “année”.

Dans l’espace utilisateur, la glibc fera beaucoup de travail pour gérer la partie “locale” de la représentation temporelle. Dans le kernel, ceci n’est pas disponible. Vous ne devriez probablement pas essayer de vous embêter avec cela dans votre module, si nécessaire, faites-le en espace utilisateur.

Un time_t est le nombre de secondes écastings depuis le 1er janvier 1970 UTC. Il n’est donc pas difficile de décomposer cela en mois, jour et année, à condition que vous souhaitiez obtenir le résultat en UTC. Il y a un tas de sources disponibles par Google “source gmtime” . La plupart des systèmes embarqués abandonnent le traitement de l’heure locale, car il est un peu plus difficile en raison de la dépendance du réglage du fuseau horaire et de l’environnement.