Apa itu TimeProvider?
System.TimeProvider adalah abstraksi waktu yang menunjukkan suatu titik waktu sebagai tipe DateTimeOffset. Dengan menggunakan TimeProvider
, Anda memastikan bahwa kode Anda dapat diuji dan dapat diprediksi.
TimeProvider
diperkenalkan di .NET 8 dan juga tersedia untuk .NET Framework 4.7+ dan .NET Standard 2.0 sebagai paket NuGet.
Kelas TimeProvider menentukan kemampuan berikut:
- Menyediakan akses ke tanggal dan waktu melalui TimeProvider.GetUtcNow() dan TimeProvider.GetLocalNow().
- Tanda waktu frekuensi tinggi dengan TimeProvider.GetTimestamp().
- Ukur waktu antara dua tanda waktu dengan TimeProvider.GetElapsedTime.
- Timer resolusi tinggi dengan TimeProvider.CreateTimer(TimerCallback, Object, TimeSpan, TimeSpan).
- Dapatkan zona waktu saat ini dengan TimeProvider.LocalTimeZone.
Implementasi default
.NET menyediakan implementasi TimeProvider melalui properti TimeProvider.System, dengan karakteristik berikut:
- Tanggal dan waktu dihitung dengan DateTimeOffset.UtcNow dan TimeZoneInfo.Local.
- Tanda waktu diberikan oleh System.Diagnostics.Stopwatch.
- Timer diimplementasikan melalui kelas internal dan diekspos sebagai System.Threading.ITimer.
Contoh berikut menunjukkan penggunaan TimeProvider untuk mendapatkan tanggal dan waktu saat ini:
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
Contoh berikut menunjukkan cara mengukur waktu yang berlalu dengan 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
Implementasi FakeTimeProvider
Paket Microsoft.Extensions.TimeProvider.Testing NuGet menyediakan implementasi TimeProvider
yang dapat dikontrol yang dirancang untuk pengujian unit.
Daftar berikut ini menjelaskan beberapa kemampuan kelas FakeTimeProvider:
- Atur tanggal dan waktu tertentu.
- Secara otomatis memajukan tanggal dan waktu dengan jumlah yang ditentukan setiap kali tanggal dan waktu dibaca.
- Lanjutkan tanggal dan waktu secara manual.
Implementasi kustom
Meskipun FakeTimeProvider harus mencakup sebagian besar skenario yang membutuhkan kepastian waktu, Anda tetap bisa memberikan implementasi Anda sendiri. Buat kelas baru yang berasal dari TimeProvider dan ambil alih anggota untuk mengontrol bagaimana waktu disediakan. Misalnya, kelas berikut hanya menyediakan satu tanggal, tanggal pendaratan bulan:
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
Jika kode yang menggunakan kelas ini memanggil MoonLandingTimeProviderPST.GetUtcNow
, tanggal pendaratan bulan di UTC dikembalikan. Jika MoonLandingTimeProviderPST.GetLocalNow
dipanggil, kelas dasar menerapkan MoonLandingTimeProviderPST.LocalTimeZone
pada GetUtcNow
dan mengembalikan tanggal dan waktu pendaratan bulan dalam zona waktu Pasifik.
Untuk menunjukkan kegunaan waktu kontrol, pertimbangkan contoh berikut. Katakanlah Anda menulis aplikasi kalender yang mengirim salam kepada pengguna saat aplikasi pertama kali dibuka setiap hari. Aplikasi ini mengatakan salam khusus ketika hari ini memiliki peristiwa yang terkait dengannya, seperti peringatan pendaratan bulan.
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
Anda mungkin cenderung menulis kode sebelumnya dengan DateTime atau DateTimeOffset untuk mendapatkan tanggal dan waktu saat ini, alih-alih TimeProvider. Namun dengan pengujian unit, sulit untuk langsung mengatasi DateTime atau DateTimeOffset. Anda harus menjalankan tes pada hari dan bulan pendaratan bulan atau lebih lanjut mengabstraksi kode menjadi unit yang lebih kecil tetapi dapat diuji.
Operasi normal aplikasi Anda menggunakan TimeProvider.System
untuk mengambil tanggal dan waktu saat ini:
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!
Dan tes unit dapat ditulis untuk menguji skenario tertentu, seperti menguji perayaan ulang tahun pendaratan bulan.
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!
Gunakan dengan .NET
Dimulai dengan .NET 8, kelas TimeProvider disediakan oleh pustaka runtime. Versi lama .NET atau pustaka yang menargetkan .NET Standard 2.0, harus mereferensikan paket Microsoft.Bcl.TimeProvider NuGet.
Metode berikut yang terkait dengan pekerjaan pemrograman asinkron dengan TimeProvider
:
- CancellationTokenSource(TimeSpan, TimeProvider)
- Task.Delay(TimeSpan, TimeProvider)
- Task.Delay(TimeSpan, TimeProvider, CancellationToken)
- Task.WaitAsync(TimeSpan, TimeProvider)
- Task.WaitAsync(TimeSpan, TimeProvider, CancellationToken)
Gunakan dengan .NET Framework
TimeProvider diimplementasikan oleh paket Microsoft.Bcl.TimeProvider NuGet.
Dukungan untuk bekerja dengan TimeProvider
dalam skenario pemrograman asinkron ditambahkan melalui metode ekstensi berikut:
- TimeProviderTaskExtensions.CreateCancellationTokenSource(TimeProvider, TimeSpan)
- TimeProviderTaskExtensions.Delay(TimeProvider, TimeSpan, CancellationToken)
- TimeProviderTaskExtensions.WaitAsync(Task, TimeSpan, TimeProvider, CancellationToken)
- TimeProviderTaskExtensions.WaitAsync<TResult>(Task<TResult>, TimeSpan, TimeProvider, CancellationToken)