TimeProvider : une alternative à DateTime.Now
Introduction
Avant la version 8 de .NET, il était courant de créer sa propre abstraction afin de pouvoir remplacer la date courante lors des tests. Et pour déterminer si une date / heure est expirée par rapport à la date et l'heure courante, nous avons l'habitude d'écrire :
public class SubscriptionService
{
public bool IsExpired(DateTime expirationDate)
{
return expirationDate <= DateTime.Now;
}
}
Depuis la version 8 de .NET, dans ce genre de situation, TimeProvider fournit désormais une abstraction standard directement intégrée à .NET, ce qui évite de maintenir une implémentation spécifique à chaque projet :
public class SubscriptionService
{
private readonly TimeProvider _timeProvider;
public SubscriptionService(TimeProvider timeProvider)
{
_timeProvider = timeProvider;
}
public bool IsExpired(DateTimeOffset expirationDate)
{
return expirationDate <= _timeProvider.GetLocalNow();
}
}
L'exemple ci-dessus utilise désormais le type DateTimeOffset, recommandé avec TimeProvider, afin de conserver également le décalage par rapport à UTC.
Une seule instance de TimeProvider.System est enregistrée dans le conteneur d'injection de dépendances, puis injectée dans les services qui ont besoin de connaître l'heure courante.
TimeProvider n'a pas vocation à remplacer systématiquement DateTime. Il prend tout son intérêt lorsque le temps influence une règle métier : expiration, relance, délai, planification, expiration de cache, etc.
Pourquoi ce choix d'implémentation ? Ce remplacement présente plusieurs intérêts, principalement lorsque la date et l'heure courantes sont utilisées dans des règles métier :
-
Meilleure abstraction : l'heure système n'est plus utilisée directement mais exposée au travers d'une abstraction (
TimeProvider) -
Meilleure testabilité : possibilité de remplacer la date / heure courante par un service qui rend la date / heure déterministe dans des tests unitaires
-
Meilleure précision sémantique :
GetLocalNow()retourne unDateTimeOffset, qui contient la date, l'heure et le décalage par rapport à UTC. Cette information permet de représenter un instant précis en conservant son décalage par rapport à UTC, ce qui limite certaines ambiguïtés liées aux fuseaux horaires et aux changements d'heure.
Aussi, de la même manière que DateTime propose la propriété UtcNow, TimeProvider propose la méthode GetUtcNow().