Mi az a TimeProvider?
System.TimeProvider az idő absztrakciója, amely DateTimeOffset típusú időpontot biztosít. A TimeProvider
használatával meggyőződhet arról, hogy a kód tesztelhető és kiszámítható. A TimeProvider
a .NET 8-ban lett bevezetve, és a .NET-keretrendszer 4.7+ és .NET Standard 2.0-hoz is elérhető NuGet-csomagként.
A TimeProvider osztály a következő képességeket határozza meg:
- Hozzáférést biztosít a dátumhoz és az időhöz TimeProvider.GetUtcNow() és TimeProvider.GetLocalNow()keresztül.
- Nagy gyakoriságú időbélyegek TimeProvider.GetTimestamp().
- Mérje meg a két időbélyeg közötti időt TimeProvider.GetElapsedTimesegítségével.
- Nagy felbontású időzítők TimeProvider.CreateTimer(TimerCallback, Object, TimeSpan, TimeSpan).
- Szerezd meg az aktuális időzónát TimeProvider.LocalTimeZone.
Alapértelmezett implementáció
A .NET a TimeProvider.System tulajdonságon keresztül biztosítja a TimeProvider implementálását a következő jellemzőkkel:
- A dátumot és az időt a DateTimeOffset.UtcNow és a TimeZoneInfo.Localalapján számítjuk ki.
- Az időbélyegeket a System.Diagnostics.Stopwatchbiztosítja.
- Az időzítők egy belső osztályon keresztül vannak implementálva, és System.Threading.ITimerjelennek meg.
Az alábbi példa bemutatja, hogy a TimeProvider használatával lekérheti az aktuális dátumot és időpontot:
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
Az alábbi példa az eltelt idő rögzítését mutatja be 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 implementáció
A Microsoft.Extensions.TimeProvider.Testing NuGet-csomag az egységteszteléshez tervezett, szabályozható TimeProvider
implementációt biztosít.
Az alábbi lista az FakeTimeProvider osztály néhány funkcióját ismerteti:
- Adjon meg egy adott dátumot és időpontot.
- A dátumot és időt automatikusan előrelépteti egy megadott összeggel, amikor a dátum és az idő beolvasása megtörténik.
- Állítsa be manuálisan a dátumot és az időt.
Egyéni megvalósítás
Bár a FakeTimeProvider a legtöbb olyan forgatókönyvet lefedi, amely időbeli kiszámíthatóságot igényel, továbbra is lehetősége van saját implementáció biztosítására. Hozzon létre egy új osztályt, amely a TimeProvider osztályból származik, és határozza meg újra a metódusokat, hogy meghatározza az idő kezelését. A következő osztály például csak egyetlen dátumot, a holdra szállás dátumát adja meg:
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
Ha az osztályt használó kód meghívja MoonLandingTimeProviderPST.GetUtcNow
, a holdra szállás dátuma (UTC) lesz visszaadva. Ha a MoonLandingTimeProviderPST.GetLocalNow
meghívásra kerül, az alaposztály alkalmazza a MoonLandingTimeProviderPST.LocalTimeZone
-t a GetUtcNow
-re, és visszaadja a holdra szállás dátumát és időpontját a PST időzóna szerint.
Az idő szabályozásának hasznosságát az alábbi példában szemlélteti. Tegyük fel, hogy egy olyan naptáralkalmazást ír, amely minden nap, amikor először megnyitják, köszöntést küld a felhasználónak. Az alkalmazás különleges üdvözlést mond, amikor az aktuális naphoz tartozik egy esemény, például a holdra szállás évfordulója.
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
Előfordulhat, hogy az előző kódot DateTime vagy DateTimeOffset megírásával szeretné lekérni az aktuális dátumot és időt TimeProviderhelyett. Az egységteszteléssel azonban nehéz megkerülni közvetlenül a(z) DateTime vagy DateTimeOffset elemeket. Le kell futtatnia a teszteket a holdra szállás napján és hónapján, vagy további kivonatolnia kell a kódot kisebb, de tesztelhető egységekre.
Az alkalmazás normál működése TimeProvider.System
használatával kéri le az aktuális dátumot és időpontot:
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!
Az egységtesztek megírhatók konkrét forgatókönyvek tesztelésére, például a holdra szállás évfordulójának tesztelésére:
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!
Használat a .NET-tel
A .NET 8-tól kezdve a TimeProvider osztályt a futtatókörnyezeti kódtár biztosítja. A .NET régebbi verzióinak vagy a .NET Standard 2.0-t célzó kódtáraknak a Microsoft.Bcl.TimeProvider NuGet-csomagrakell hivatkozniuk.
Az aszinkron programozáshoz kapcsolódó alábbi módszerek a TimeProvider
-tal dolgoznak:
- CancellationTokenSource(TimeSpan, TimeProvider)
- Task.Delay(TimeSpan, TimeProvider)
- Task.Delay(TimeSpan, TimeProvider, CancellationToken)
- Task.WaitAsync(TimeSpan, TimeProvider)
- Task.WaitAsync(TimeSpan, TimeProvider, CancellationToken)
Használat a .NET-keretrendszerrel
A TimeProvider
aszinkron programozási forgatókönyvekben való használatának támogatása a következő bővítménymetelyekkel lett hozzáadva:
- TimeProviderTaskExtensions.CreateCancellationTokenSource(TimeProvider, TimeSpan)
- TimeProviderTaskExtensions.Delay(TimeProvider, TimeSpan, CancellationToken)
- TimeProviderTaskExtensions.WaitAsync(Task, TimeSpan, TimeProvider, CancellationToken)
- TimeProviderTaskExtensions.WaitAsync<TResult>(Task<TResult>, TimeSpan, TimeProvider, CancellationToken)