web-dev-qa-db-fra.com

Spécificateurs de format pour les types définis par l'implémentation comme time_t

Je veux rendre mon code plus indépendant de la plateforme/de l'implémentation. Je ne sais pas ce qu'un time_t sera implémenté comme sur la plateforme lors de la compilation du code. Comment connaître le type de t pour déterminer le spécificateur de format à utiliser?

...
time_t t = time(NULL);
printf("%s", t);
...
24

Généralement, la façon d'afficher la valeur d'un time_t consiste à décomposer ses composants en struct tm en utilisant gmtime ou localtime et affichez-les ou convertissez-les comme vous le souhaitez avec strftime, ou ctime pour passer directement de time_t à une chaîne indiquant l'heure locale.

Si vous voulez voir la valeur brute dans un certain but, la norme C spécifie que time_t est réel, ce qui signifie qu'il est entier ou à virgule flottante (C 2011 (N1570) 6.2.5 17). Par conséquent, vous devriez pouvoir le convertir en double et l'imprimer. Il est possible que time_t peut représenter des valeurs que double ne peut pas, donc vous devrez peut-être vous en prémunir si vous voulez faire attention aux implémentations exotiques. Puisque difftime renvoie la différence de deux time_t objets comme double, il semble que C ne supporte pas vraiment time_t avec plus de précision qu'un double.

10
Eric Postpischil

Habituellement, vous pouvez utiliser un transtypage pour convertir l'opérande en un type dont vous connaissez le bon format.

Votre solution proposée:

time_t t = time(NULL);
printf("%s", t);

clairement pas fonctionnera, puisque time_t est un type numérique, pas char*.

Nous savons, en général, que time_t Est un type arithmétique. Quelque chose comme ça:

 printf("%ld\n", (long)t);

est susceptible de fonctionner sur la plupart des systèmes. Il peut échouer (a) si time_t Est un type non signé pas plus large que unsigned long Et que la valeur actuelle de t dépasse LONG_MAX, Ou (b) si time_t Est un type à virgule flottante.

Si vous avez le support C99, vous pouvez utiliser long long, Ce qui est un peu mieux:

printf("%lld\n", (long long)t);

Si vous voulez vraiment aller trop loin avec la portabilité, vous pouvez détecter le type de type time_t:

if ((time_t)-1 > 0) {
    // time_t is an unsigned type
    printf("%ju\n", (uintmax_t)t);
}
else if ((time_t)1 / 2 > 0) {
    // time_t is a signed integer type
    printf("%jd\n", (intmax_t)t);
}
else {
    // time_t is a floating-point type (I've never seen this)
    printf("%Lg\n", (long double)t);
}

Vous pouvez souhaiter ajuster le format %Lg À quelque chose comme %Lf Ou %.10Lf, Selon le format de sortie que vous souhaitez.

Encore une fois, cela suppose la prise en charge de C99 - et vous aurez besoin de #include <stdint.h> Pour rendre uintmax_t Et intmax_t Visibles.

time_t Et clock_t Sont un peu inhabituels, en ce sens que la norme indique seulement qu'ils sont de type arithmétique capables de représenter les temps. (En principe, il pourrait s'agir de types complexes, mais je dirais qu'ignorer cette possibilité en vaut le risque.)

Dans la plupart des autres cas, vous saurez probablement si un type donné est signé, non signé ou à virgule flottante, et vous pouvez simplement convertir le type le plus large de ce type.

Notez que si vous ne savez pas comment time_t Est représenté, vous ne comprendrez probablement pas non plus la sortie du printf (comme 1379375215) - à moins que votre objectif est de comprendre cela.

(Si vous programmiez en C++ plutôt qu'en C, std::cout << t << "\n"; Utiliserait automatiquement la bonne surcharge operator<<.)

Si vous voulez une sortie lisible par l'homme (comme Mon 2013-09-16 16:46:55 PDT), Vous voudrez utiliser l'une des fonctions de conversion déclarées dans <time.h>, Comme asctime() ou strftime().

24
Keith Thompson

Vous pouvez utiliser difftime() pour obtenir un double:

time_t t = time(NULL);
printf("seconds 1970->now: %.f\n", difftime(t, (time_t) 0));

C'est simple et je pense que c'est portable.

6
chus

La norme C indique que time_t Sera un "type réel" (c'est-à-dire un type entier ou un type à virgule flottante, bien qu'en pratique ce soit toujours un type entier).

Avec time_t, Le mieux est de le formater avec strftime() après l'avoir analysé avec localtime() ou gmtime() - cela peut être fait de manière portable.

Malheureusement, vous devez déterminer par un mécanisme quel est le spécificateur de format correct. Vous pouvez utiliser PRI_[Xxodi]_time Et SCN_[Xxodi]_time Ou quelque chose de similaire comme non standard mais proche de la norme (sans piétiner l'espace de noms réservé - c'est-à-dire les noms commençant par PRI ou SCN suivi d'une lettre minuscule ou X). Vous utilisez un mécanisme pour spécifier que ... encapsulant les informations non transférables en un seul endroit.

3
Jonathan Leffler