Partage via


Qu’est-ce que TimeProvider ?

System.TimeProvider est une abstraction du temps qui fournit un point dans le temps en tant que type DateTimeOffset. En utilisant TimeProvider, vous assurez que votre code est testable et prévisible. TimeProvider a été introduit dans .NET 8 et est également disponible pour .NET Framework 4.7+ et .NET Standard 2.0 en tant que package NuGet.

La classe TimeProvider définit les fonctionnalités suivantes :

Implémentation par défaut

.NET fournit une implémentation de TimeProvider via la propriété TimeProvider.System, avec les caractéristiques suivantes :

L’exemple suivant montre comment utiliser TimeProvider pour obtenir la date et l’heure actuelles :

Console.WriteLine($"Local: {TimeProvider.System.GetLocalNow()}");
Console.WriteLine($"Utc:   {TimeProvider.System.GetUtcNow()}");

/* This example produces output similar to the following:
 *
 * Local: 12/5/2024 10:41:14 AM -08:00
 * Utc:   12/5/2024 6:41:14 PM +00:00
*/
Console.WriteLine($"Local: {TimeProvider.System.GetLocalNow()}")
Console.WriteLine($"Utc:   {TimeProvider.System.GetUtcNow()}")

' This example produces output similar to the following
'
' Local: 12/5/2024 10:41:14 AM -08:00
' Utc:   12/5/2024 6:41:14 PM +00:00

L’exemple suivant illustre la capture du temps écoulé avec TimeProvider.GetTimestamp():

long stampStart = TimeProvider.System.GetTimestamp();
Console.WriteLine($"Starting timestamp: {stampStart}");

long stampEnd = TimeProvider.System.GetTimestamp();
Console.WriteLine($"Ending timestamp:   {stampEnd}");

Console.WriteLine($"Elapsed time: {TimeProvider.System.GetElapsedTime(stampStart, stampEnd)}");
Console.WriteLine($"Nanoseconds: {TimeProvider.System.GetElapsedTime(stampStart, stampEnd).TotalNanoseconds}"); 

/* This example produces output similar to the following:
 *
 * Starting timestamp: 55185546133
 * Ending timestamp:   55185549929
 * Elapsed time: 00:00:00.0003796
 * Nanoseconds: 379600
*/
Dim stampStart As Long = TimeProvider.System.GetTimestamp()
Console.WriteLine($"Starting timestamp: {stampStart}")

Dim stampEnd As Long = TimeProvider.System.GetTimestamp()
Console.WriteLine($"Ending timestamp:   {stampEnd}")

Console.WriteLine($"Elapsed time: {TimeProvider.System.GetElapsedTime(stampStart, stampEnd)}")
Console.WriteLine($"Nanoseconds: {TimeProvider.System.GetElapsedTime(stampStart, stampEnd).TotalNanoseconds}")

' This example produces output similar to the following:
'
' Starting timestamp: 55185546133
' Ending timestamp:   55185549929
' Elapsed time: 00:00:00.0003796
' Nanoseconds: 379600

Implémentation de FakeTimeProvider

Le package NuGet Microsoft.Extensions.TimeProvider.Testing fournit une implémentation TimeProvider contrôlable conçue pour les tests unitaires.

La liste suivante décrit certaines des fonctionnalités de la classe FakeTimeProvider :

  • Définissez une date et une heure spécifiques.
  • Avancez automatiquement la date et l’heure par un montant spécifié chaque fois que la date et l’heure sont lues.
  • Avancez manuellement la date et l’heure.

Implémentation personnalisée

Bien que FakeTimeProvider couvre la plupart des scénarios nécessitant une prévisibilité avec le temps, vous pouvez toujours fournir votre propre implémentation. Créez une nouvelle classe qui dérive de TimeProvider et remplacez les membres pour contrôler la façon dont le temps est fourni. Par exemple, la classe suivante fournit uniquement une date unique, la date de l’atterrissage lunaire :

public class MoonLandingTimeProviderPST: TimeProvider
{
    // July 20, 1969, at 20:17:40 UTC
    private readonly DateTimeOffset _specificDateTime = new(1969, 7, 20, 20, 17, 40, TimeZoneInfo.Utc.BaseUtcOffset);

    public override DateTimeOffset GetUtcNow() => _specificDateTime;

    public override TimeZoneInfo LocalTimeZone => TimeZoneInfo.FindSystemTimeZoneById("PST");
}
Public Class MoonLandingTimeProviderPST
    Inherits TimeProvider

    'July 20, 1969, at 20:17:40 UTC
    Private ReadOnly _specificDateTime As New DateTimeOffset(1969, 7, 20, 20, 17, 40, TimeZoneInfo.Utc.BaseUtcOffset)

    Public Overrides Function GetUtcNow() As DateTimeOffset
        Return _specificDateTime
    End Function

    Public Overrides ReadOnly Property LocalTimeZone As TimeZoneInfo
        Get
            Return TimeZoneInfo.FindSystemTimeZoneById("PST")
        End Get
    End Property

End Class

Si le code utilisant cette classe appelle MoonLandingTimeProviderPST.GetUtcNow, la date de l’atterrissage lunaire au format UTC est retournée. Si MoonLandingTimeProviderPST.GetLocalNow est appelée, la classe de base applique MoonLandingTimeProviderPST.LocalTimeZone à GetUtcNow et retourne la date et l’heure d’atterrissage de la lune dans le fuseau horaire PST.

Pour illustrer l’utilité du contrôle du temps, considérez l’exemple suivant. Supposons que vous écrivez une application de calendrier qui envoie un message d’accueil à l’utilisateur lorsque l’application est ouverte chaque jour. L’application indique un message d’accueil spécial lorsque la journée actuelle a un événement associé à celui-ci, tel que l’anniversaire de l’atterrissage lunaire.

public static class CalendarHelper
{
    static readonly DateTimeOffset MoonLandingDateTime = new(1969, 7, 20, 20, 17, 40, TimeZoneInfo.Utc.BaseUtcOffset);
    
    public static void SendGreeting(TimeProvider currentTime, string name)
    {
        DateTimeOffset localTime = currentTime.GetLocalNow();

        Console.WriteLine($"Good morning, {name}!");
        Console.WriteLine($"The date is {localTime.Date:d} and the day is {localTime.Date.DayOfWeek}.");

        if (localTime.Date.Month == MoonLandingDateTime.Date.Month
            && localTime.Date.Day == MoonLandingDateTime.Date.Day)
        {
            Console.WriteLine("Did you know that on this day in 1969 humans landed on the Moon?");
        }

        Console.WriteLine($"I hope you enjoy your day!");
    }
}
Public Module CalendarHelper

    ReadOnly MoonLandingDateTime As DateTimeOffset = #7/20/1969 20:17:40#

    Public Sub SendGreeting(currentTime As TimeProvider, name As String)

        Dim localTime As DateTimeOffset = currentTime.GetLocalNow()

        Console.WriteLine($"Good morning, {name}!")
        Console.WriteLine($"The date is {localTime.Date:d} and the day is {localTime.Date.DayOfWeek}.")

        If (localTime.Date.Month = MoonLandingDateTime.Date.Month _
            And localTime.Date.Day = MoonLandingDateTime.Date.Day) Then

            Console.WriteLine("Did you know that on this day in 1969 humans landed on the Moon?")
        End If

        Console.WriteLine($"I hope you enjoy your day!")

    End Sub

End Module

Vous pouvez être enclin à écrire le code précédent avec DateTime ou DateTimeOffset pour obtenir la date et l’heure actuelles, au lieu de TimeProvider. Mais avec des tests unitaires, il est difficile de contourner DateTime ou DateTimeOffset directement. Vous devrez soit exécuter les tests le jour et le mois de l’atterrissage lunaire, soit extraire davantage le code en unités plus petites mais testables.

L’opération normale de votre application utilise TimeProvider.System pour récupérer la date et l’heure actuelles :

CalendarHelper.SendGreeting(TimeProvider.System, "Eric Solomon");

/* This example produces output similar to the following:
 *
 * Good morning, Eric Solomon! 
 * The date is 12/5/2024 and the day is Thursday. 
 * I hope you enjoy your day! 
*/
CalendarHelper.SendGreeting(TimeProvider.System, "Eric Solomon")

' This example produces output similar to the following:
'
' Good morning, Eric Solomon! 
' The date is 12/5/2024 and the day is Thursday. 
' I hope you enjoy your day!

Et les tests unitaires peuvent être écrits pour tester des scénarios spécifiques, tels que le test de l’anniversaire de l’atterrissage lunaire :

CalendarHelper.SendGreeting(new MoonLandingTimeProviderPST(), "Eric Solomon");

/* This example produces output similar to the following:
 *
 * Good morning, Eric Solomon!
 * The date is 7/20/1969 and the day is Sunday.
 * Did you know that on this day in 1969 humans landed on the Moon?
 * I hope you enjoy your day!
*/
CalendarHelper.SendGreeting(New MoonLandingTimeProviderPST(), "Eric Solomon")

' This example produces output similar to the following:
'
' Good morning, Eric Solomon!
' The date is 7/20/1969 and the day is Sunday.
' Did you know that on this day in 1969 humans landed on the Moon?
' I hope you enjoy your day!

Utiliser avec .NET

À compter de .NET 8, la classe TimeProvider est fournie par la bibliothèque runtime. Les anciennes versions de .NET ou les bibliothèques ciblant .NET Standard 2.0 doivent faire référence au package NuGet Microsoft.Bcl.TimeProvider.

Les méthodes suivantes liées à la programmation asynchrone fonctionnent avec TimeProvider:

Utiliser avec .NET Framework

TimeProvider est implémenté par le package NuGet Microsoft.Bcl.TimeProvider.

La prise en charge de l’utilisation de TimeProvider dans les scénarios de programmation asynchrone a été ajoutée via les méthodes d’extension suivantes :