Ескертпе
Бұл бетке кіру үшін қатынас шегін айқындау қажет. Жүйеге кіруді немесе каталогтарды өзгертуді байқап көруге болады.
Бұл бетке кіру үшін қатынас шегін айқындау қажет. Каталогтарды өзгертуді байқап көруге болады.
System.TimeProvider — это абстракция времени, которая предоставляет точку во времени в виде типа DateTimeOffset. Используя TimeProvider, вы гарантируете, что код можно тестировать и прогнозировать.
TimeProvider доступен на следующих платформах:
| Платформа | Примечания. |
|---|---|
| .NET 8+ | Включена в фреймворк. |
| .NET 5 — .NET 7 | Предоставляется в пакетеMicrosoft.Bcl.TimeProvider NuGet. |
| .NET Framework 4.6.2+ | Предоставляется в пакете NuGet. |
| .NET Standard 2.0 | Предоставляется в пакете NuGet. |
Класс TimeProvider определяет следующие возможности:
- Предоставляет доступ к дате и времени через TimeProvider.GetUtcNow() и TimeProvider.GetLocalNow().
- Высокочастотные метки времени с TimeProvider.GetTimestamp().
- Измерение времени между двумя метками времени с помощью TimeProvider.GetElapsedTime.
- Таймеры высокого разрешения с TimeProvider.CreateTimer(TimerCallback, Object, TimeSpan, TimeSpan).
- Определите текущий часовой пояс с помощью TimeProvider.LocalTimeZone.
Реализация по умолчанию
.NET предоставляет реализацию TimeProvider через свойство TimeProvider.System со следующими характеристиками:
- Дата и время вычисляются с помощью DateTimeOffset.UtcNow и TimeZoneInfo.Local.
- Метки времени предоставляются System.Diagnostics.Stopwatch.
- Таймеры реализованы с использованием внутреннего класса и представлены в виде System.Threading.ITimer.
В следующем примере показано, как использовать 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
Пакет NuGet Microsoft.Extensions.TimeProvider.Testing предоставляет управляемую реализацию TimeProvider, предназначенную для модульного тестирования.
В следующем списке описаны некоторые возможности класса FakeTimeProvider:
- Задайте определенную дату и время.
- Автоматически перемещайте дату и время по указанному количеству при чтении даты и времени.
- Вручную измените дату и время.
Настраиваемая реализация
Хотя FakeTimeProvider охватывает большинство сценариев, требующих прогнозируемости с течением времени, вы по-прежнему можете обеспечить собственную реализацию. Создайте новый класс, производный от TimeProvider, и переопределите элементы, чтобы управлять предоставлением времени. Например, следующий класс предоставляет только одну дату, дату посадки луны:
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 вызывается, базовый класс применяет MoonLandingTimeProviderPST.LocalTimeZone к GetUtcNow и возвращает дату и время посадки на Луну в часовом поясе Тихоокеанского стандартного времени (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
Возможно, вы склонны писать предыдущий код с DateTime или DateTimeOffset, чтобы получить текущую дату и время вместо TimeProvider. Но с модульным тестированием трудно обойти DateTime или DateTimeOffset напрямую. Вам потребуется либо запустить тесты в день и месяц посадки луны, либо дополнительно абстрагировать код в меньшие, но тестируемые единицы.
Обычная операция приложения использует 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 или библиотек, предназначенные для .NET Standard 2.0, должны ссылаться на Microsoft.Bcl.TimeProvider пакет NuGet.
Следующие методы, связанные с асинхронным программированием, работают с TimeProvider:
- CancellationTokenSource(TimeSpan, TimeProvider)
- Task.Delay(TimeSpan, TimeProvider)
- Task.Delay(TimeSpan, TimeProvider, CancellationToken)
- Task.WaitAsync(TimeSpan, TimeProvider)
- Task.WaitAsync(TimeSpan, TimeProvider, CancellationToken)
Использование с .NET Framework
Пакет Microsoft.Bcl.TimeProvider NuGet реализует TimeProvider.
Добавлена поддержка работы с TimeProvider в сценариях асинхронного программирования с помощью следующих методов расширения:
- TimeProviderTaskExtensions.CreateCancellationTokenSource(TimeProvider, TimeSpan)
- TimeProviderTaskExtensions.Delay(TimeProvider, TimeSpan, CancellationToken)
- TimeProviderTaskExtensions.WaitAsync(Task, TimeSpan, TimeProvider, CancellationToken)
- TimeProviderTaskExtensions.WaitAsync<TResult>(Task<TResult>, TimeSpan, TimeProvider, CancellationToken)