次の方法で共有


TimeProvider とは

System.TimeProvider は、特定の時点を DateTimeOffset 型として提供する時間の抽象化です。 TimeProviderを使用すると、コードがテスト可能で予測可能であることを確認できます。 TimeProvider は .NET 8 で導入され、NuGet パッケージとして .NET Framework 4.7 以降および .NET Standard 2.0 でも使用できます。

TimeProvider クラスは、次の機能を定義します。

既定の実装

.NET では、TimeProvider.System プロパティを使用した TimeProvider の実装が提供され、次の特性があります。

次の例では、TimeProvider を使用して現在の日付と時刻を取得する方法を示します。

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

次の例は、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

FakeTimeProvider の実装

Microsoft.Extensions.TimeProvider.Testing NuGet パッケージ は、単体テスト用に設計された制御可能な TimeProvider 実装を提供します。

次の一覧では、FakeTimeProvider クラスの機能の一部について説明します。

  • 特定の日付と時刻を設定します。
  • 日付と時刻を読み取るたびに、指定した量の日付と時刻を自動的に進める。
  • 日付と時刻を手動で進める。

カスタム実装

FakeTimeProvider は、時間と共に予測可能性を必要とするほとんどのシナリオをカバーする必要がありますが、それでも独自の実装を提供することもできます。 TimeProvider から派生する新しいクラスを作成し、メンバーをオーバーライドして時間の指定方法を制御します。 たとえば、次のクラスは、月面着陸の日付である 1 つの日付のみを提供します。

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

このクラスを使用するコードが MoonLandingTimeProviderPST.GetUtcNowを呼び出すと、UTC での月面着陸の日付が返されます。 MoonLandingTimeProviderPST.GetLocalNow が呼び出されると、基底クラスは GetUtcNowMoonLandingTimeProviderPST.LocalTimeZone を適用し、PST タイムゾーンの月着陸日時を返します。

時間の制御の有用性を示すために、次の例を考えてみましょう。 たとえば、アプリが毎日初めて開かれるときに、ユーザーにあいさつを送信する予定表アプリを作成しているとします。 アプリは、現在の日が月着陸の記念日など、それに関連付けられているイベントがあるときに特別な挨拶を言います。

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

以前のコードを記述する際に、TimeProviderではなく、DateTime または DateTimeOffset を使用して現在の日時を取得することを検討してみてください。 ただし、単体テストでは、DateTimeDateTimeOffset を直接回避するのは困難です。 月着陸の日と月にテストを実行するか、コードをさらに小さいがテスト可能な単位に抽象化する必要があります。

アプリの通常の操作では、TimeProvider.System を使用して現在の日時を取得します。

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!

また、月面着陸記念日のテストなど、特定のシナリオをテストするために単体テストを記述できます。

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!

.NET での使用

.NET 8 以降では、TimeProvider クラスはランタイム ライブラリによって提供されます。 .NET Standard 2.0 をターゲットとする以前のバージョンの .NET またはライブラリは、NuGet パッケージ Microsoft.Bcl.TimeProvider を参照する必要があります。

非同期プログラミングに関連する次のメソッドは、TimeProviderで動作します。

.NET Framework での使用

TimeProvider は、Microsoft.Bcl.TimeProvider NuGet パッケージによって実装されます。

非同期プログラミング シナリオでの TimeProvider の操作のサポートは、次の拡張メソッドを使用して追加されました。