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 ;
0 si c'est l'heure d'hiver ;
+1 si c'est l'heure d'été. |
- |
? : 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
Code : C - Exemple : Remplir une struct tm 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31 | #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 :
Code : Console - Et le Site du Zéro fut ! | 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 :
Code : C - Prototype de clock()
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…
Secret (cliquez pour afficher)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.
Il est possible que clock() retourne -1.
Si c'est le cas, c'est qu'il y a eu une erreur. Prévoyez ce cas dans vos futurs programmes.
Bon, assez parlé, testons
clock().
Code : C - Exemple : Récupérer le tick 0 | #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 ?
Code : Console - Un résultat nul dans les deux sens du terme
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().
Code : C - Récupérer un certain tick | #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 !
Code : C - Plus ou Moins avec un chronomètre 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28 | #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.
Code : Console - Ce qu'affichent les résultats sous Windows 32 bits (1000 CLOCKS_PER_SEC) | 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 :
Code : C | void attendre(float temps);
|
Le temps sera bien sûr en secondes (et pas en siècles…).
C'est parti !
Correction
Secret (cliquez pour afficher)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.
Code : C - Exemple d'attente 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26 | #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 :
Code : C - Macro ATTENDRE() portable | #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…
Code : C - Prototype de time() | 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.
Il est possible que time() retourne -1.
Si c'est le cas, c'est qu'elle n'a pas réussi à récupérer la valeur de temps.
Prévoyez ce cas dans vos futurs programmes.
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() :
Code : C - Exemple : Le temps interne de votre ordinateur | #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;
}
|
Code : Console | 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 :
Code : C - Prototype de localtime() | 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 via localtime().
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.
Code : C - Exemple : Horloge 1
2
3
4
5
6
7
8
9
10
11
12
13
14 | #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.
Code : Console - Vous avez un gros indice pour le top spécial quizz
Notez qu'il existe une fonction pour directement mettre notre struct tm en forme : asctime().
Mais nous verrons tout ça dans la troisième partie.
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.
Code : C - Prototype de gmtime() | struct tm* gmtime (const time_t *temps);
|
Cette fonction prend en paramètre le nombre de secondes depuis le 1
er 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.
Code : C - Des macros utiles pour le décalage horaire | #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.
Code : C - Exemple : Le voyage Sydney-New York 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 | #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
via time() 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.
Code : Console | 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.