Twitter iPhone pliant OnePlus 11 PS5 Disney+ Orange Livebox Windows 11

Dates 1900 -> 1970

20 réponses
Avatar
candide
Bonjour,

La Norme propose en exemple la recherche du jour de la semaine du 4 juillet 2001
(le C est américain jusqu'au bout ;) mais je les félicite de leur choix
d'exemple, à nouveau la Norme est beaucoup plus didactique qu'on ne le dit ne
général). Voici leur code mais adapté pour la recherche du jour de la semaine
pour un siècle plutôt :

/* -------------------------------------------------- */
#include <stdio.h>
#include <time.h>

static const char *const wday[] =
{ "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday",
"Saturday", "-unknown-"
};


int main(void)
{
struct tm time_str;

time_str.tm_year = 1901 - 1900;
time_str.tm_mon = 7 - 1;
time_str.tm_mday = 4;
time_str.tm_hour = 0;
time_str.tm_min = 0;
time_str.tm_sec = 1;
time_str.tm_isdst = -1;

if (mktime(&time_str) == (time_t) (-1))
time_str.tm_wday = 7;

printf("%s\n", wday[time_str.tm_wday]);
return 0;
}
/* -------------------------------------------------- */


Et quelle n'est pas ma surprise de voir l'affichage suivant :

-unknown-

Vérification faite, sur ma machine, le dernier jour qui ne marche pas est le
vendredi 13 (!!) décembre 1901 et ça me renvoie bien Sunday pour le 14 décembre
1901, on dirait le bug de 2038 avec des nombres négatifs (2038-1970=62 et
1970-62= 1902 et en fait environ décembre 1901 car le bug est en janvier 2038).

Je suis très étonné de cette impossibilité de répondre car je pensais que la
conversion était assurée pour les dates postérieures à 1900, cf. ce que dit la
Norme :

7.23 Date and time <time.h> 7.23.1 Components of time
1 (...) The semantics of the members and their normal ranges are expressed in
the comments.

tm_year; // years since 1900

[Au passage, je trouve assez curieux de donner des informations de spécification
dans des commentaires de code].

Sous Windows chez moi, c'est encore pire, ça ne marche qu'à partir du 2 janvier
1970 (Mingw et Visual C++).

Un compilateur conforme est-il censé traiter de toutes les dates depuis 1900 ?
[si je regarde le code de Plauger dans son livre, il fait attention à ce que ces
dates soient traitées entre 1801 et 2099].

10 réponses

1 2
Avatar
Eric Levenez
Le 05/03/09 22:26, dans <49b04372$0$15788$,
« candide » a écrit :

Je suis très étonné de cette impossibilité de répondre car je pensais que la
conversion était assurée pour les dates postérieures à 1900,



1900 est juste l'offset, pas la date de départ, l'"Epoch". La date de départ
est généralement 1970, mais ce n'est pas dans la norme C, mais dans la norme
Unix. La date de fin de validité est souvent, hélas, 2038.

--
Éric Lévénez
FAQ de fclc : <http://www.levenez.com/lang/c/faq/>
Avatar
Antoine Leca
Le 05/03/2009 21:26, candide écrivit :
Vérification faite, sur ma machine, le dernier jour qui ne marche pas est le
vendredi 13 (!!) décembre 1901 et ça me renvoie bien Sunday pour le 14 décembre
1901, on dirait le bug de 2038 avec des nombres négatifs (2038-1970b et
1970-62= 1902 et en fait environ décembre 1901 car le bug est en janvier 2038).



L'analyse est correcte, il s'agit bien du même problème.

Je suis très étonné de cette impossibilité



Tu ne devrais pas trop.
Les timestamps *nix (dont sont dérivés nombre d'implémentations du type
time_t) sont conçus pour dater des évènements intéressant la machine ; à
partir de là, et sachant qu'il faut bien fixer une limite quelque part,
il ne faut pas s'étonner que seuls les évènements ultérieurs au 1er
janvier 1970 (le début de l'ère Posix) se doivent d'être représentables;
de la même façon, il ne devrait pas être trop problématique
*AUJOURD'HUI* d'être limité dans le futur à la présentation des
évènements jusqu'en 2037 "seulement".

Note bien qu'actuellement, les implémentations profitent des plus
grandes possibilités offertes par les matériels pour graduellement
étendre le domaine couvert ; seulement, à l'inverse du battage
médiatique organisé par les SSII au sujet du « bogue de l'an 2000 », la
transition se fait en douceur, en essayant de préserver le plus possible
les fonctionnalités existantes là où cela peut poser problème (donc pas
de big bang pour avoir time2_t comme un compte de décimicrosecondes
depuis les début de l'ère grégorienne, comme certains le propos[èr]ent).
Alors effectivement cela signifie que certaines implémentations peuvent
paraître aujourd'hui limitées, mais cela ne devrait pas être un problème
dans la pratique.


Un autre problème très différent, et qui affecte surtout C plus que
*nix, est l'utilisation faite par certains programmes du type time_t
pour représenter des évènements sur une plus grande échelle ; un exemple
typique ici est celui des dates de naissance, et avec difftime() on peut
calculer plus facilement les horoscopes... Évidemment, il s'agit là
d'une perversion.


Un compilateur conforme est-il censé traiter de toutes les dates depuis 1900 ?



Non. En fait il y a mêmes des implémentations classiques et conformes
(sur ce point) qui ne remontent pas avant 1980...

De plus, tu as mélangé time_t et les spécifications de struct tm ; hors
rien n'oblige à ce le premier couvre le même domaine que le second, et
surtout pas les spécifications de mktime() (la fonction que passe de
l'autre à l'un) qui prévoit explicitement des cas où la date-et-heure-du
calendrier ne peut être représenté...


Antoine
Avatar
candide
Antoine Leca a écrit :

surtout pas les spécifications de mktime() (la fonction que passe de
l'autre à l'un) qui prévoit explicitement des cas où la date-et-heure-du
calendrier ne peut être représenté...



OK, merci de ta réponse. Ce que je retiens de tout ça, c'est en particulier
qu'il faut tester le retour de mktime().
Avatar
candide
Eric Levenez a écrit :


1900 est juste l'offset, pas la date de départ, l'"Epoch".



Oui, effectivement, on peut voir les choses comme ça.
Avatar
Xavier Roche
candide a écrit :
Sous Windows chez moi, c'est encore pire, ça ne marche qu'à partir du 2 janvier
1970 (Mingw et Visual C++).



A priori c'est un bug de la libc (enfin, de la couche de
""compatibilité"" POSIX) sous Windows, qui ne gère pas correctement les
valeurs négatives (time_t est signé, même si ce n'est pas toujours
explicitement dit)

A noter que, en général, sizeof(time_t) == sizeof(void*) (la limite de
2038/1902 n'est ainsi vraie qu'en 32-bit -- on peut ainsi espérer éviter
le bug de l'an 2038 avec le remplacement progressif des 32-bit par du
64-bit pur)
Avatar
Eric Levenez
Le 06/03/09 18:32, dans <gormmu$cfm$, « Xavier Roche »
a écrit :

A noter que, en général, sizeof(time_t) == sizeof(void*) (la limite de



En général sizeof(time_t) == sizeof(long int), ce qui revient parfois au
même :-)

2038/1902 n'est ainsi vraie qu'en 32-bit -- on peut ainsi espérer éviter
le bug de l'an 2038 avec le remplacement progressif des 32-bit par du
64-bit pur)



Avec un time_t de 64 bits il ne devrait pas y avoir de problème après 2038.
Mais les programmes 32 bits auront sûrement des problèmes (même sur les OS
64 bits).

Je pense qu'un time_t négatif est possible (après 2038), mais comme
certaines fonctions retournent -1, beaucoup de programmes ont fait du code
du type :

if (mktime(x) < 0)

Au lieu de :

if (mktime(x) == (time_t)-1)

--
Éric Lévénez
FAQ de fclc : <http://www.levenez.com/lang/c/faq/>
Avatar
Xavier Roche
Eric Levenez a écrit :
A noter que, en général, sizeof(time_t) == sizeof(void*) (la limite de


En général sizeof(time_t) == sizeof(long int), ce qui revient parfois au
même :-)



Hélas sizeof(long int) == 4 dans tous les cas sous WIN32, c'est pour ça
que j'avais pris sizeof(void*) pour dire "quand le code est 64-bit, en
général time_t est aussi en 64-bit".

Je pense qu'un time_t négatif est possible (après 2038), mais comme
certaines fonctions retournent -1, beaucoup de programmes ont fait du code
du type :
if (mktime(x) < 0)



Bon alors en fait c'est beaucoup plus hilarant, car -1 n'est pas
vraiment un code d'erreur.

Indice:
* (time_t) 0 c'est "01 Jan 1970 00:00:00 GMT""
* (time_t) -1 c'est "31 Dec 1969 23:59:59 GMT"

Bon alors là on prend un cachet d'aspirine, et on jette un oeil à POSIX
en priant:

<http://www.opengroup.org/onlinepubs/009695399/functions/mktime.html>
"If the time since the Epoch cannot be represented, the function shall
return the value (time_t)-1 and may set errno to indicate the error."

Perdu: si ça renvoi (time_t) -1, "démerdez-vous avec l'implémentation".

Typiquement sous WIN32, il est sage d'aller calculer à la main le mktime
avec les fonctions WIN32 (SystemTimeToFileTime et KB167296), et sous
Linux on peut se permettre royalement:

errno = 0;
if ( ( t = mktime(x) ) != (time_t) -1 || errno == 0) {
.. c'est tout bon
}
Avatar
Eric Levenez
Le 06/03/09 19:54, dans <gorrhr$cfm$, « Xavier Roche »
a écrit :

Eric Levenez a écrit :
A noter que, en général, sizeof(time_t) == sizeof(void*) (la limite de


En général sizeof(time_t) == sizeof(long int), ce qui revient parfois au
même :-)



Hélas sizeof(long int) == 4 dans tous les cas sous WIN32, c'est pour ça
que j'avais pris sizeof(void*) pour dire "quand le code est 64-bit, en
général time_t est aussi en 64-bit".



Mais sous Unix, Gnu/Linux, Mac OS X... time_t est quasiment tout le temps un
typedef de "long" (c'est en fait dur de trouver une autre définition). Le
monde Windows est à part bien sûr...

Je pense qu'un time_t négatif est possible (après 2038), mais comme
certaines fonctions retournent -1, beaucoup de programmes ont fait du code
du type :
if (mktime(x) < 0)



Bon alors en fait c'est beaucoup plus hilarant, car -1 n'est pas
vraiment un code d'erreur.

Indice:
* (time_t) 0 c'est "01 Jan 1970 00:00:00 GMT""
* (time_t) -1 c'est "31 Dec 1969 23:59:59 GMT"



Si, si, -1 est bien un code d'erreur, et c'est l'implémentation qui défini
les limites d'utilisation des fonctions pour que le -1 ne soit pas une
valeur valide. Ainsi sur une machine typique 32 bits, la plage d'utilisation
va de 1970 à 2038, et le 1969 que tu donnes est hors borne.

--
Éric Lévénez
FAQ de fclc : <http://www.levenez.com/lang/c/faq/>
Avatar
Xavier Roche
Eric Levenez a écrit :
Si, si, -1 est bien un code d'erreur, et c'est l'implémentation qui défini
les limites d'utilisation des fonctions pour que le -1 ne soit pas une
valeur valide. Ainsi sur une machine typique 32 bits, la plage d'utilisation
va de 1970 à 2038, et le 1969 que tu donnes est hors borne.



Non, non. C'est bien de 1900-et-des-poussières à 2038, time_t est
_vraiment_ un entier signé.

1980: time_t == 315529200
1970: time_t == -3600
1960: time_t == -315622800
1950: time_t == -631155600
1940: time_t == -946771200
1930: time_t == -1262304000
1920: time_t == -1577923200
1910: time_t == -1893456561


/* mktime() */
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <errno.h>
#include <string.h>

int main(void) {
int i;
struct tm tm;

memset(&tm, 0, sizeof(tm));
tm.tm_mday = 1;
for(i = 1980 ; i > 1900 ; i -= 10) {
time_t t;
tm.tm_year = i - 1900;
if ( ( t = mktime(&tm) ) != (time_t) -1 || errno == 0 ) {
printf("%u: time_t == %dn", i, (int) t);
} else {
abort();
}
}

return 0;
}
Avatar
Eric Levenez
Le 06/03/09 20:29, dans <gorti0$cfm$, « Xavier Roche »
a écrit :

Eric Levenez a écrit :
Si, si, -1 est bien un code d'erreur, et c'est l'implémentation qui défini
les limites d'utilisation des fonctions pour que le -1 ne soit pas une
valeur valide. Ainsi sur une machine typique 32 bits, la plage d'utilisation
va de 1970 à 2038, et le 1969 que tu donnes est hors borne.



Non, non. C'est bien de 1900-et-des-poussières à 2038, time_t est
_vraiment_ un entier signé.



Tu généralises ton implémentation. Certaines implémentations gèrent les
time_t négatif (hors -1), d'autres pas. Il est vrai que sur les systèmes de
type unix, time_t est normalement un entier signé, mais ce n'est pas une
règle. Sur QNX, le valeur est non signée, et permet donc d'aller jusqu'à
2106, et pas mal de systèmes embarqués avaient fait ce choix. Sur les OS
grand public, la règle semble être : "on passera en 64 bits, et ça sera
réglé".

--
Éric Lévénez
FAQ de fclc : <http://www.levenez.com/lang/c/faq/>
1 2