Dans cette partie nous allons voir :
comment obtenir la date et l'heure actuelle ;
comment situer son programme dans le temps (et ainsi créer un chronomètre, par exemple).
struct tm : Structure du temps
Pour manipuler les dates et les heures, il nous faut de quoi stocker le temps de manière compréhensible, c'est-à-dire qu'au lieu d'une seule variable contenant des secondes (on se retrouverait avec des modulos partout), nous aurons une structure contenant un instant mais utilisant les différentes unités et informations que l'on emploie (année, mois, jour du mois, jour de la semaine, jour de l'année, heure, minute, seconde et si nous sommes en heure d'été ou d'hiver).
struct tm
répond à ce besoin. Il s'agit d'une structure contenant les variables suivantes.
Variable | Représente | Rang |
---|---|---|
tm_year | Le nombre d'années depuis 1900. | 0 - ? |
tm_mon | Mois écoulés depuis janvier (n° du mois-1). | 0 - 11 |
tm_yday | Nombre de jours écoulés depuis le 1er janvier (n° jour-1). | 0 - 365 |
tm_mday | Numéro du jour du mois. | 1 - 31 |
tm_wday | Nombre de jours écoulés depuis dimanche (et non lundi). | 0 - 6 |
tm_hour | Nombre d'heures écoulées depuis minuit. | 0 - 23 |
tm_min | Nombre de minutes écoulées depuis le dernier changement d'heure. | 0 - 59 |
tm_sec | Nombre de secondes écoulées depuis le dernier changement de minute. | 0 - 60* |
tm_isdst | -1 si l'information est indisponible ; | - |
? : Dépend de la façon dont la date est gérée par votre système d'exploitation.
* : Certains systèmes nécessitent une marge pour éviter des bugs. Néanmoins, retenez 0 - 59. Cette marge n'a pas vraiment d'importance.
Cette structure correspond donc à la représentation d'un instant avec de nombreuses informations simples à exploiter ! Nous n'allons pas encore la remplir automatiquement suivant l'heure interne de votre ordinateur, mais je vous propose un code d'exemple pour comprendre son utilisation (pas très propre mais c'est pour l'exemple).
Exemple
#include <stdio.h>
#include <time.h>
int main(void)
{
struct tm instant;
int remplir;
printf("Entrez le mois : ");
scanf("%d", &remplir);
instant.tm_mon = remplir-1;
printf("Entrez le jour : ");
scanf("%d", &remplir);
instant.tm_mday = remplir-1;
printf("Entrez l'heure : ");
scanf("%d", &remplir);
instant.tm_hour = remplir;
printf("Entrez la minute : ");
scanf("%d", &remplir);
instant.tm_min = remplir;
printf("Entrez la seconde : ");
scanf("%d", &remplir);
instant.tm_sec = remplir;
printf("%d/%d ; %d:%d:%d", instant.tm_mday+1, instant.tm_mon+1, instant.tm_hour, instant.tm_min, instant.tm_sec);
return 0;
}
Pour les directives de préprocesseur, on inclut bien sûr stdio.h pour les fonctions printf()
et scanf()
et time.h pour notre struct tm
. stdlib.h n'est pas nécessaire dans notre cas.
Dans la fonction int main(void)
(void
signifie que la fonction ne prend rien), on a la déclaration de la structure instant de type struct tm
et la variable « remplir » qui nous servira à supprimer un décalage par la suite. Puis on demande à l'utilisateur d'entrer des valeurs qui seront conservées dans les variables appropriées en passant par « remplir ». Grâce à « remplir », on supprime le décalage présent pour le mois et le jour. Pour les heures, minutes et secondes, ce n'est pas la peine. Pour remplir la structure, on remplit chaque variable en la précédant du nom de la structure et d'un point. Les structures vous sont expliquées dans le cours officiel.
Au sein de la fonction printf()
, on a une mise en forme des valeurs que l'on a remplies. Remarquez que les variables tm_mday et tm_mon ne sont pas affichées telles qu'elles sont mais avec une incrémentation afin d'obtenir les vrais numéros du mois et du jour. En fait, cela sert à remettre tout ça dans les bonnes valeurs. Ces problèmes de décalages peuvent être assez déroutants, mais on comprend très vite grâce au tableau situé au début de cette partie.
Voici ce que ça peut donner :
Entrez le mois : 11
Entrez le jour : 10
Entrez l'heure : 0
Entrez la minute : 0
Entrez la seconde : 0
10/11 ; 0:0:0
Exercice
Ajoutez les années à mon code. Attention, il y a un piège !
clock() : Situer son programme dans le temps
clock()
est un bidule assez intéressant. Il permet de situer son programme dans le temps. Voici son prototype :
clock_t clock (void);
Cette fonction ne prend donc aucun paramètre et renvoie un nombre sous un nouveau type : le clock_t
. Le type clock_t
permet d'exprimer un nombre de clock ticks. Utilisez donc ce type lorsque vous souhaitez utiliser des clock ticks (comme le type size_t
fait référence à une taille).
Le clock tick (ou battement pour les puristes du français) est une unité de mesure du temps interne de votre ordinateur. Qu'est-ce que ça représente par rapport à la seconde ? Eh bien…
Vous l'avez deviné : ça dépend !
En fait, cette valeur dépend de votre système d'exploitation et de votre processeur. time.h possède une macro permettant savoir combien de clock ticks par seconde votre ordinateur gère. Il s'agit de la macro CLOCKS_PER_SEC.
Bon, assez parlé, testons clock()
.
#include <stdio.h>
#include <time.h>
int main(void)
{
printf("%f\n", (double) clock());
return 0;
}
Ce code est un programme tout bête qui affiche la valeur retournée par clock()
. Remarquez le (double)
avant l'appel de la fonction. Il permet de transformer les clock_t
renvoyés en double
afin d'éviter un beau warning. On appelle cette action le cast (un cast en (int)
marche, mais néanmoins il arrive que ce type contienne un nombre décimal, alors on utilise %f
avec un cast(double)
, mais attention, ça ne marche pas dans tous les cas).
Qu'affiche-t-il ?
0
Aïe. Quelque chose ne va pas dans ton code.
Laisse-moi deviner. Comme clock()
est appelée dès le début, elle renvoie le début du programme, soit 0 clock tick ?
Exactement ! Il va donc falloir mettre une attente avant l'appel de clock()
.
#include <stdio.h>
#include <time.h>
int main(void)
{
getchar();
printf("%f\n", (double) clock());
return 0;
}
Un getchar()
vous demandera d'appuyer sur la touche Entrée avant de continuer et d'afficher la valeur renvoyée par clock()
.
C'est bien beau, mais tu as dit que cette fonction était intéressante.
Or, ça ne sert à rien.
Bien au contraire ! Réfléchissez bien. Cela permet de faire un chronomètre par exemple ! Voyez cette nouvelle version du Plus ou Moins !
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
int main(void)
{
int mystere, nombreentre;
clock_t temps;
srand(time(NULL));
mystere=rand()%100+1;
do{
puts("Quel est le nombre ? ");
scanf("%d", &nombreentre);
if(nombreentre>mystere)
puts("C'est moins !\n");
if(nombreentre<mystere)
puts("C'est plus !\n");
if(nombreentre==mystere){
temps=clock();
printf("Bravo !\nTu as mis %f secondes a trouver le nombre.\n", (double) temps/CLOCKS_PER_SEC);
}
}while(nombreentre!=mystere);
return 0;
}
C'est le même logiciel (je l'ai un peu simplifié mais c'est le même principe) sauf que l'on a ici une variable temps de type clock_t
et lorsque le nombre est trouvé, on lui donne la bonne valeur via la fonction clock()
. Après cela, on affiche les résultats grâce au fameux %f
et une division temps/CLOCKS_PER_SEC
sans oublier le cast (double)
avant pour éviter un warning. Grâce à cette division, on obtient des secondes et non des clock ticks.
Quel est le nombre ?
61
Bravo !
Tu as mis 13.156000 secondes a trouver le nombre.
Exercice
On peut faire un chronomètre, mais aussi une attente ! Créez une fonction permettant de faire une attente et qui aura ce prototype :
void attendre(float temps);
Le temps sera bien sûr en secondes (et pas en siècles…).
C'est parti !
Correction
Il fallait utiliser une boucle vide se répétant tant que clock()
ne renvoie pas une valeur que l'on a prédite à l'avance via un petit calcul. J'ai réalisé, en plus de cette fonction, un petit programme pour tester.
#include <stdio.h>
#include <time.h>
void attendre(float temps);
int main(void)
{
int compteur;
for(compteur=10;compteur>0;compteur--){
printf("%d...\n", compteur);
attendre(1);
}
puts("BONNE ANNEE !!!\n");
return 0;
}
void attendre(float temps)
{
clock_t arrivee=clock()+(temps*CLOCKS_PER_SEC); // On calcule le moment où l'attente devra s'arrêter
while(clock()<arrivee);
}
Pour calculer le moment où l'attente s'arrête, on prend le moment dans lequel on se trouve (le début de l'attente) puis on y ajoute le temps d'attente en le transformant en clock ticks (d'où la multiplication par CLOCKS_PER_SEC).
Par contre, cette attente n'est pas aussi géniale que l'on pourrait le croire. En effet, elle consomme beaucoup de CPU. Il existe deux fonctions plus propres et moins gourmandes pour faire une attente : sleep()
et Sleep()
(la majuscule fait toute la différence).
sleep()
est dans le header unistd.h (Linux et Mac uniquement) et Sleep()
dans windows.h (vous savez pour quel système d'exploitation !). De plus elles ne prennent pas la même unité : Sleep()
attend des millisecondes d'après sa page sur la MSDN et sleep()
des secondes. Voici donc un code préprocesseur vous permettant d'obtenir une fonction attendre propre et portable sous forme de macro :
#if defined (WIN32) || defined (WIN64)
#include <windows.h>
#define ATTENDRE(temps) Sleep(temps*1000)
#else
#include <unistd.h>
#define ATTENDRE(temps) sleep(temps)
#endif
time() : Obtenir la date et l'heure
Attention, nous allons enfin voir comment remplir une struct tm
avec l'heure interne de votre ordinateur. Mais nous avons besoin de passer par deux fonctions pour ce faire. La première est time()
. Elle est souvent utilisée dans la génération de nombres pseudo-aléatoires (voyez le Plus ou Moins). Mais que fait-elle ? Voyons son prototype…
time_t time(time_t *variable);
Un nouveau type ! time_t
est un type de variable qui sert à indiquer un nombre de secondes.
Bon ! En fait, time() a un fonctionnement assez bizarre : faire variable=time(NULL)
revient à faire time(&variable)
(n'oubliez pas le &, sinon, warning). time()
va mettre la valeur à renvoyer directement dans la variable dans les deux cas (d'où l'utilisation du NULL
dans le premier cas, vu qu'il n'y a aucune variable à remplir en paramètre).
Et ce qu'elle renvoie, ça correspond à quoi ?
La valeur renvoyée correspond au nombre de secondes écoulées depuis le premier janvier 1970. Non, cette date ne correspond à rien d'autre que le début du calendrier sur la plupart des systèmes d'exploitation.
Juste comme ça, pourquoi l'utilise-t-on pour générer des nombres pseudo-aléatoires ?
srand()
demande un nombre pour l'initialiser car si on lui donne le même nombre, la fonction génèrera la même suite de nombres. Alors on utilise le temps car il est très rare de lancer deux fois le même programme dans la même seconde.
Voyons un exemple tout bête de la fonction time()
:
#include <stdio.h>
#include <time.h>
int main(void)
{
printf("Il s'est ecoule %f secondes depuis le 1er janvier 1970.\n", (double) time(NULL));
return 0;
}
Il s'est ecoule 1298819112 secondes depuis le 1er janvier 1970.
Spécial quizz : devinez à quel moment j'ai lancé mon programme sans utiliser les fonctions que nous verrons par la suite !
localtime() : Utiliser ce qui est renvoyé par time() pour remplir une struct tm
time()
, c'est bien, mais on ne peut pas en faire grand chose. localtime()
va nous permettre d'utiliser time()
pour remplir une struct tm
. Voici son prototype :
struct tm* localtime (const time_t *secondes);
Elle prend un nombre de secondes pour renvoyer un pointeur vers une struct tm
. Et devinez comment on fait pour remplir une struct tm
avec l'heure actuelle ? C'est simple ! On récupère d'abord le temps actuel grâce à time()
puis on remplit notre struct tm
vialocaltime()
.
La valeur renvoyée est un pointeur vers une zone mémoire allouée statiquement et partagée entre les fonctions gmtime()
, localtime()
et ctime()
. Vous pouvez donc utiliser des pointeurs pour ne pas avoir à copier la valeur de cette zone mémoire. Attention cependant à utiliser cela correctement car les précédentes valeurs de la zone mémoire seront écrasées lors des prochains appels de ces fonctions.
#include <stdio.h>
#include <time.h>
int main(void)
{
time_t secondes;
struct tm instant;
time(&secondes);
instant=*localtime(&secondes);
printf("%d/%d ; %d:%d:%d\n", instant.tm_mday+1, instant.tm_mon+1, instant.tm_hour, instant.tm_min, instant.tm_sec);
return 0;
}
C'est le même code que lorsque je vous ai appris à remplir une struct tm
sauf que, cette fois-ci, les fonctions le font pour nous. Remarquez que pour remplir « instant », j'utilise un pointeur. Si je ne mets pas cet astérisque (*), on renvoie un pointeur dans une struct tm
, ce qui provoque une erreur lors de la compilation.
28/2 ; 16:32:28
gmtime() : Obtenir l'heure GMT
Lire l'article GMT sur Wikipédia
L'heure GMT, c'est l'heure qu'il est en Grande Bretagne, chez nos amis les anglais. Or, en France, au Québec ou en Australie ce n'est pas la même heure. Mais en utilisant gmtime()
, on peut récupérer l'heure GMT.
struct tm* gmtime (const time_t *temps);
Cette fonction prend en paramètre le nombre de secondes depuis le 1er janvier 1970 dans votre fuseau horaire (pas besoin de conversion) et renvoie l’adresse mémoire de time.h destinée aux struct tm
(reportez-vous à la partie sur localtime()
) après l'avoir remplie avec l'heure GMT. Par exemple en France, ce sera une heure avant puisque la France se situe dans le fuseau horaire +1.
Son utilité ? Imaginez que vous habitiez à Sydney en Australie et que vous souhaitiez prendre l'avion pour New York aux États-Unis. Vous savez combien de temps dure le trajet mais vous voulez arriver dans une fourchette d'heures bien précise (disons entre 13h et 15h, heure de New York). Vous connaissez la durée du trajet, oui mais pas l'heure qu'il est là-bas. gmtime()
vous permettra de la trouver avec des calculs très simples.
Voici la représentation du monde avec les différents fuseaux horaires.
Représentation du monde avec les différents fuseaux horaires.
Image tirée de Wikipédia, œuvre placée dans le domaine public.
Sydney se trouve donc dans un fuseau horaire GST (GMT+10) et New York dans un fuseau horaire EST (GMT-5). Cela signifie que lorsqu'il est minuit à Greenwich — ville où a été placé le méridien d'origine et donc le fuseau horaire UDC (celui correspondant à l'heure GMT) —, il est 10 heures à Sydney et 19h à New York.
#define SYD 10
#define NYC -5
Un moyen simple est de créer des macros correspondant aux fuseaux horaires (certains fuseaux horaires ont des noms civils et militaires mais pour des raisons de simplicité, j'ai mis les noms des villes Sydney et New York City) correspondant au décalage horaire par rapport à GMT.
Tableau répertoriant les noms civils et militaires des fuseaux horaires
#include <stdio.h>
#include <time.h>
#define SYD 10
#define NYC -5
int main(void)
{
time_t temps;
struct tm date;
time(&temps);
date=*gmtime(&temps);
printf("GMT: %d:%d\n", date.tm_hour, date.tm_min);
printf("Sydney: %d:%d\n", (date.tm_hour+SYD+24)%24, date.tm_min);
printf("New-York: %d:%d\n", (date.tm_hour+NYC+24)%24, date.tm_min);
return 0;
}
Après avoir placé nos macros et initialisé nos variables, on récupère l'heure GMT actuelle viatime()
et gmtime()
. Ensuite, on place des printf()
qui doivent nous indiquer l'heure GMT, l'heure de Sydney et l'heure de New York. Pour la calculer, on fait le calcul (heure+décalage+24)%24
. Le modulo est nécessaire car si on ne le met pas, on pourrait se retrouver avec des heures comme 26:30. Et je n'aimerais pas créer de faille spatio-temporelle. De même pour le +24 avant le modulo. Si avec la soustraction de l'heure pour le fuseau horaire de New York on obtient une heure négative, il va falloir la transformer en un nombre positif entre 0 et 24. Pour ce faire, on ajoute tout simplement 24. Le modulo supprimera les heures en trop s'il y en a.
GMT: 17:1
Sydney: 3:1
New-York: 12:1
Entre nous, je crois que c'est fichu pour aller à New York aujourd'hui.
En tout cas, on en a enfin fini avec les différentes manières d'obtenir des informations relatives au temps. La partie 2 porte sur quelques manipulations et calculs réalisables avec les informations obtenues.