Partilhar via


O que é TimeProvider?

System.TimeProvider é uma abstração de tempo que fornece um ponto no tempo como um tipo de DateTimeOffset. Ao usar TimeProvider, você garante que seu código seja testável e previsível. TimeProvider foi introduzido no .NET 8 e também está disponível para .NET Framework 4.7+ e .NET Standard 2.0 como um pacote NuGet.

A classe TimeProvider define os seguintes recursos:

Implementação padrão

O .NET fornece uma implementação de TimeProvider através da propriedade TimeProvider.System, com as seguintes características:

O exemplo a seguir demonstra o uso de TimeProvider para obter a data e hora atuais:

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

O exemplo a seguir demonstra a captura do tempo decorrido com 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

Implementação FakeTimeProvider

O pacote Microsoft.Extensions.TimeProvider.Testing NuGet fornece uma implementação de TimeProvider controlável projetada para testes de unidade.

A lista a seguir descreve alguns dos recursos da classe FakeTimeProvider:

  • Defina uma data e hora específicas.
  • Adiante automaticamente a data e a hora por um valor especificado sempre que a data e a hora forem lidas.
  • Adiante manualmente a data e a hora.

Implementação personalizada

Embora FakeTimeProvider deva cobrir a maioria dos cenários que exigem previsibilidade com o tempo, ainda pode fornecer a sua própria implementação. Crie uma nova classe que deriva de TimeProvider e substitua membros para controlar como o tempo é fornecido. Por exemplo, a classe a seguir fornece apenas uma única data, a data do pouso na lua:

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

Se o código que usa esta classe chamar MoonLandingTimeProviderPST.GetUtcNow, a data do pouso na lua em UTC é retornada. Se MoonLandingTimeProviderPST.GetLocalNow for chamado, a classe base aplica MoonLandingTimeProviderPST.LocalTimeZone a GetUtcNow e devolve a data e hora de alunagem no fuso horário do Pacífico (PST).

Para demonstrar a utilidade de controlar o tempo, considere o exemplo a seguir. Digamos que você esteja escrevendo um aplicativo de calendário que envia uma saudação ao usuário quando o aplicativo é aberto pela primeira vez todos os dias. O aplicativo diz uma saudação especial quando o dia atual tem um evento associado a ele, como o aniversário do pouso na lua.

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

Você pode estar inclinado a escrever o código anterior com DateTime ou DateTimeOffset para obter a data e hora atuais, em vez de TimeProvider. No entanto, com os testes unitários, é difícil lidar diretamente com DateTime ou DateTimeOffset. Você precisaria executar os testes no dia e mês do pouso na lua ou abstrair o código em unidades menores, mas testáveis.

A operação normal do seu aplicativo usa TimeProvider.System para recuperar a data e a hora atuais:

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!

E testes de unidade podem ser escritos para testar cenários específicos, como testar o aniversário do pouso na Lua:

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!

Usar com .NET

A partir do .NET 8, a classe TimeProvider é fornecida pela biblioteca de execução. Versões mais antigas do .NET ou bibliotecas destinadas ao .NET Standard 2.0 devem fazer referência ao pacote Microsoft.Bcl.TimeProvider NuGet.

Os seguintes métodos relacionados à programação assíncrona funcionam com TimeProvider:

Utilizar com o .NET Framework

TimeProvider é implementado pelo Microsoft.Bcl.TimeProvider pacote NuGet.

O suporte para trabalhar com TimeProvider em cenários de programação assíncrona foi adicionado através dos seguintes métodos de extensão: