2017-03-30 3 views
2

J'écris une fonction qui, entre autres choses, doit imprimer un compteur de temps écoulé. Il reçoit par référence une heure de début _start, et la compare à current, les deux étant tapés time_t. Je veux utiliser strftime() pour imprimer le delta de temps observé au format ISO 8601. Voici ce que je tentais de le faire:Temps écoulé formaté en C

// Negative start time implies program has not begun 
if (*_start > 0) { 
    time_t elapsed = current - *_start; 
    strftime(time_str, sizeof(time_str) - 1, "%T", localtime(&elapsed)); 
    printf("%s\n", time_str); 
} 

Et voici la sortie que je reçois immédiatement après l'exécution du programme

01:00:00 
01:00:00 
01:00:01 
01:00:01 
01:00:01 

Les secondes fonctionnent très bien, si je laisse courir plus ils incrémentée comme prévu, alors faites les minutes, cependant l'heure commence comme 01 par opposition à 00. Pourquoi cela se produit-il? Comment puis-je commencer à zéro les heures, comme les minutes?

+1

En utilisant 'localtime' vous vraiment obtenir la date depuis minuit 1 janvier 1970 * dans votre région fuseau horaire*. Par exemple, ici sur la côte ouest des États-Unis si 'elapsed_time = 0' alors' strftime' dit 16:00:00 parce que 'localtime (0)' est ici 'Wed Dec 31 16:00:00 1969'. Je suppose que vous êtes en Europe continentale. Vous utiliseriez 'gmtime' à la place, mais cela ne fonctionnerait que jusqu'à 24 heures. – Schwern

Répondre

3

time_t généralement (voir modification) stocke les horodatages absolus (nombre de secondes écoulées depuis minuit UTC, 1er janvier 1970). En calculant current - *_start, vous obtenez le temps écoulé en secondes (comme vous le souhaitez), mais en passant ensuite cela à localtime et strftime, vous dites à l'ordinateur de prendre le temps écoulé depuis le début de votre programme et de le traiter comme le temps écoulé depuis minuit UTC 1-1-1970.

Je suppose qu'il se trouve 01:00:00 dans le fuseau horaire de votre système.

Je ne suis pas au courant d'une fonction C99 pour imprimer le temps écoulé, mais il n'est pas difficile d'en écrire une vous-même.

void format_elapsed_time(char *time_str, int total_seconds_elapsed) { 
    int hours_elapsed = total_seconds_elapsed/3600; 
    int minutes_elapsed = (total_seconds_elapsed % 3600)/60; 
    int seconds_elapsed = total_seconds_elapsed % 60; 
    sprintf(time_str, "%02i:%02i:%02i", hours_elapsed, minutes_elapsed, seconds_elapsed); 
} 

Modifier: Comme @chux souligne, time_t ne doit pas stocker horodatages en secondes depuis 01.01.1970. Voir this answer pour plus de détails.

+0

Cela ne gèrera pas les secondes intercalaires d'ailleurs :-) Je ne sais pas pourquoi vous n'avez pas simplement suggéré 'gmtime' plutôt que' localtime'. – paxdiablo

+1

'time_t' est très commun comme le nombre de secondes depuis le 1er janvier 1970, mais cela n'est pas spécifié par C.' time_t' pourrait être n'importe quel type réel, y compris ['double'] (http://stackoverflow.com/q/2792551/2410359) à partir de n'importe quelle époque. – chux

+0

@chux - Merci pour la correction. –

1

Pour trouver le portably nombre de secondes écoulées entre 2 time_t, utilisez difftime() comme soustraction de 2 time_t valeurs ne sont pas spécifiées dans C à une différence en quelques secondes.

double difftime(time_t time1, time_t time0); C11dr §7.26.2.2
La fonction difftime renvoie la différence exprimée en secondes comme double.

double elapsed_seconds = difftime(current, *_start); 
printf("elapsed_seconds: %f\n", elapsed_seconds); 
1

Si vous souhaitez utiliser cette méthode, vous devez être conscient que time() UTC retours. Si vous soustrayez ensuite cela de votre local heure (tel que renvoyé par localtime()), les fuseaux horaires auront un effet sur le résultat (il est probable que votre fuseau horaire particulier soit supprimé de l'heure UTC d'une heure).

Tenir compte du programme complet ci-dessous, similaire à votre extrait:

#include <stdio.h> 
#include <time.h> 
#include <unistd.h> 

int main(void) { 
    time_t start = time(0); 
    char time_str[100]; 
    for (int i = 5; i > 0; i--) { 
     sleep (1); 
     time_t elapsed = time(0) - start; 
     strftime(time_str, sizeof(time_str) - 1, "%T", localtime(&elapsed)); 
     printf("%s\n", time_str); 
    } 

    return 0; 
} 

considèrent également que mon fuseau horaire particulier est retiré de l'UTC de huit heures:

pax> date ; date -u 
Thursday 30 March 10:25:57 AWST 2017 
Thursday 30 March 02:25:57 UTC 2017 

Quand je cours, je voir:

08:00:01 
08:00:02 
08:00:03 
08:00:04 
08:00:05 

Vous pouvez voir là que le décalage horaire de huit heures de U TC affecte la valeur. Le correctif pour cela est en fait assez simple: n'utilisez pas du tout l'heure locale. Si vous remplacez l'appel localtime() avec gmtime(), vous obtenez la sortie que vous attendez:

00:00:01 
00:00:02 
00:00:03 
00:00:04 
00:00:05 
+0

Mineure: Comme je l'ai lu "Si le nombre total de caractères résultants, y compris le caractère nul final, n'est pas supérieur à maxsize, la fonction strftime renvoie le nombre de caractères placés dans le tableau pointé par s (..., sizeof (time_str) - 0, ...); 'est suffisant. Tes pensées? – chux

0

Je me rends compte de cette question a déjà une réponse, mais je pensais que je pourrais détourner l'attention un peu et d'élargir la réponse acceptée. Étant donné que l'OP a indiqué qu'il traite fois écoulé, on peut éviter de traiter des horodatages absolus en utilisant la fonction clock().

clock_t start = clock(); 
. 
. 
clock_t end = clock(); 
double elapsed = (end - start)/(double)CLOCKS_PER_SEC; 

alors vous pouvez appeler la fonction format_elapsed_time fictivement révisée(), comme suit:

void format_elapsed_time(char *time_str, double elapsed) { 
    int h, m, s, ms; 

    h = m = s = ms = 0; 
    ms = elapsed * 1000; // promote the fractional part to milliseconds 
    h = ms/3600000; 
    ms -= (h * 3600000); 
    m = ms/60000; 
    ms -= (m * 60000); 
    s = ms/1000; 
    ms -= (s * 1000); 
    sprintf(time_str, "%02i:%02i:%02i.%03i", h, m, s, ms); 
}