Horloge
La mesure du temps dans un programme est une de ces tâches qui semble simple au
premier abord, mais se révèle complexe quand on y regarde de plus près.
L’évolution des APIs de gestion du temps de C++ en témoigne : parti des
fonctionnalités simples du C, C++
a graduellement accumulé une API très
complexe en essayant de supporter
toutes les utilisations avancées de l’horloge système pour lesquelles time()
et clock()
se révèlent être trop simplistes.
Rust a adopté ici une approche nettement plus minimaliste : la vocation du
module std::time
de la
bibliothèque standard Rust n’est pas de répondre à tous les besoins possibles et
imaginables de gestion du temps,
si
obscurs
soient-ils,
mais de fournir juste ce qu’il faut pour…
- Mesurer précisément le temps écoulé pendant l’exécution du programme.
- Connaître l’heure système UTC et pouvoir la comparer entre deux processus et avec les différentes timestamps d’accès aux fichiers.
Les fonctionnalités plus avancées, comme la gestion des fuseaux horaires ou le
formatage des dates/heures, sont déléguées à des bibliothèques externes
spécialisées comme
time
et
chrono
.
Mesure du temps écoulé
L’équivalent Rust de la fonction
clock()
du C et de
l’horloge
steady_clock
de C++
est le type Instant
.
C’est une horloge monotone : elle part d’un point arbitraire dans le passé,
n’est pas modifiée quand l’heure système est modifiée, et on peut donc
l’utiliser quand on veut savoir combien de temps prennent les opérations
effectuées par le programme.
La conception est similaire à celle de std::chrono
en C++, mais avec une API
qui est beaucoup plus simple, tout en restant assez puissante pour tous les
besoins courants :
- A tout moment, on peut demander l’heure qu’il est du point de vue de cette
horloge avec
Instant::now()
. Cette information est représentée par un objet de typeInstant
. - Connaissant deux
Instant
s, on peut calculer le temps écoulé entre les deux en les soustrayant. Ce temps écoulé est représenté par un objet de typeDuration
. - De façon symétrique, on peut ajouter ou supprimer une
Duration
à unInstant
pour obtenir un autreInstant
qui représente la valeur de l’horloge attendue N secondes avant/après la mesure de temps précédemment effectuée.
Voici un exemple d’utilisation de Instant
et Duration
:
#![allow(unused)] fn main() { use std::time::{Instant, Duration}; // Exécution minutée let debut = Instant::now(); println!("Affichage d'un texte"); let duree = debut.elapsed(); // Analyse du temps d'exécution println!("L'affichage a pris {duree:?}"); assert!(duree < Duration::from_millis(100)); // printf n'est pas si lent ! }
Heure système UTC
Instant
ne permet pas de répondre à la question utilisateur “Quelle heure
est-il ?”, car son point d’origine est arbitraire : ça peut être l’allumage de
l’ordinateur, le lancement du programme, le dernier redémarrage vraiment
complet…
c’est dépendant de l’OS qu’on est en train d’utiliser. Il y a même des
divergences d’opinions entre les OS sur la pertinence de continuer à mesurer ou
pas le temps écoulé quand l’ordinateur passe en veille (la bonne approche dépend
du besoin).
L’horloge Rust qui permet de se raccorder au temps humain, comme la fonction
time()
en C et l’horloge
system_clock
en C++,
s’appelle SystemTime
.
Elle s’utilise un peu comme Instant
, mais elle a en plus un point de référence
UNIX_EPOCH
qui permet d’en déduire la date/heure UTC.
Les précautions usuelles concernant l’heure système s’appliquent :
- Gardez à l’esprit que l’horloge système n’est pas forcément à l’heure. Il peut être dangereux de présumer qu’elle l’est si, par exemple, la sécurité de votre programme en dépend.
- Il est peu probable que UTC soit le fuseau horaire de l’utilisateur. On doit donc éviter d’afficher des heures UTC dans des messages destinés à ce dernier, sinon il y a un risque de confusion.
- L’horloge système est sujette à se décaler brutalement vers le passé ou l’avenir si l’utilisateur ou un système de mise à l’heure automatique change sa valeur. Ce n’est donc pas une horloge adaptée pour mesurer des durées d’exécution au sein du programme.
Voici un exemple d’utilisation de SystemTime
:
#![allow(unused)] fn main() { use std::time::SystemTime; let dt = SystemTime::UNIX_EPOCH .elapsed() .expect("Non, on n'est pas plus tôt que le 1/1/1970 !"); println!("Il s'est passé {dt:?} depuis l'epoch Unix"); }
On le voit, les fonctionnalités de manipulation de l’heure système de la
bibliothèque standard Rust sont minimalistes, et on a fortement intérêt à les
compléter avec des bibliothèques comme
time
et
chrono
si on a l’intention de faire
quoi que ce soit d’un peu complexe comme afficher des dates/heures à
l’utilisateur.
Cette conception différente s’explique par la facilité d’utilisation des bibliothèques tierce partie en Rust, via Cargo, qui évite d’encombrer la bibliothèque standard de fonctionnalités avancées au risque de réduire sa portabilité entre matériels et systèmes d’exploitation.