From 262c00ff96ca72d8197a947dd4513b91f69a956d Mon Sep 17 00:00:00 2001
From: "Daniel Mackay [SSW]" <2636640+danielmackay@users.noreply.github.com>
Date: Sun, 5 May 2024 13:54:41 +1000
Subject: [PATCH] =?UTF-8?q?=F0=9F=90=9B=2016=20fix=20bug=20to=20do=20with?=
=?UTF-8?q?=20timestamps=20when=20running=20in=20docker=20(#17)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
* Added SydneyTimeProvider
* Remove weather component
* Removed usages of DateTime.Now
* Register SydneyTimeProvider
* Added unit tests
* Refactored get timesheet to use common utc calculation
* Refactored timesheet query to use common UTC logic
* Tidy up from review
---
DailyScrumGenerator.sln | 7 ++
global.json | 7 +-
.../Common/Services/SydneyTimeProvider.cs | 10 +++
src/WebUI/Common/Services/TimeProviderExt.cs | 36 ++++++++++
src/WebUI/Components/Pages/Weather.razor | 66 -------------------
.../DailyScrum/Components/Pages/Debug.razor | 19 ++++++
.../DailyScrum/Components/Pages/Index.razor | 10 ++-
.../Features/DailyScrum/DailyScrumFeature.cs | 2 +
.../DailyScrum/Infrastructure/GraphService.cs | 7 +-
.../DailyScrum/Queries/GetDailyScrumQuery.cs | 37 +++++------
.../Timesheet/Components/Pages/Index.razor | 11 +++-
.../Queries/GetTimeSheetNotesQuery.cs | 34 +++++-----
tests/IntegrationTests/GraphServiceTests.cs | 9 ++-
tests/UnitTests/GlobalUsings.cs | 1 +
tests/UnitTests/SydneyTimeProviderTests.cs | 16 +++++
tests/UnitTests/TimeProviderExtTests.cs | 37 +++++++++++
tests/UnitTests/UnitTests.csproj | 30 +++++++++
17 files changed, 224 insertions(+), 115 deletions(-)
create mode 100644 src/WebUI/Common/Services/SydneyTimeProvider.cs
create mode 100644 src/WebUI/Common/Services/TimeProviderExt.cs
delete mode 100644 src/WebUI/Components/Pages/Weather.razor
create mode 100644 src/WebUI/Features/DailyScrum/Components/Pages/Debug.razor
create mode 100644 tests/UnitTests/GlobalUsings.cs
create mode 100644 tests/UnitTests/SydneyTimeProviderTests.cs
create mode 100644 tests/UnitTests/TimeProviderExtTests.cs
create mode 100644 tests/UnitTests/UnitTests.csproj
diff --git a/DailyScrumGenerator.sln b/DailyScrumGenerator.sln
index 804dc7f..c53ae52 100644
--- a/DailyScrumGenerator.sln
+++ b/DailyScrumGenerator.sln
@@ -16,6 +16,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WebUI", "src\WebUI\WebUI.cs
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "IntegrationTests", "tests\IntegrationTests\IntegrationTests.csproj", "{30037715-AA77-4D4F-B961-8DE69E9B7A28}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UnitTests", "tests\UnitTests\UnitTests.csproj", "{7E4AFE3C-FF99-4E27-8DA5-B0F55F806AA7}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -30,9 +32,14 @@ Global
{30037715-AA77-4D4F-B961-8DE69E9B7A28}.Debug|Any CPU.Build.0 = Debug|Any CPU
{30037715-AA77-4D4F-B961-8DE69E9B7A28}.Release|Any CPU.ActiveCfg = Release|Any CPU
{30037715-AA77-4D4F-B961-8DE69E9B7A28}.Release|Any CPU.Build.0 = Release|Any CPU
+ {7E4AFE3C-FF99-4E27-8DA5-B0F55F806AA7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {7E4AFE3C-FF99-4E27-8DA5-B0F55F806AA7}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {7E4AFE3C-FF99-4E27-8DA5-B0F55F806AA7}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {7E4AFE3C-FF99-4E27-8DA5-B0F55F806AA7}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{351FF09F-D517-4930-8D86-607DB4B7904C} = {92C5999C-A447-479E-8630-064E6FDC78DA}
{30037715-AA77-4D4F-B961-8DE69E9B7A28} = {21C89A08-D942-45BE-A302-4EEB543573C0}
+ {7E4AFE3C-FF99-4E27-8DA5-B0F55F806AA7} = {21C89A08-D942-45BE-A302-4EEB543573C0}
EndGlobalSection
EndGlobal
diff --git a/global.json b/global.json
index 989a69c..3639463 100644
--- a/global.json
+++ b/global.json
@@ -1,6 +1,7 @@
{
"sdk": {
- "version": "8.0.100",
- "rollForward": "latestMinor"
+ "version": "8.0.0",
+ "rollForward": "latestFeature",
+ "allowPrerelease": false
}
-}
\ No newline at end of file
+}
diff --git a/src/WebUI/Common/Services/SydneyTimeProvider.cs b/src/WebUI/Common/Services/SydneyTimeProvider.cs
new file mode 100644
index 0000000..e8c6671
--- /dev/null
+++ b/src/WebUI/Common/Services/SydneyTimeProvider.cs
@@ -0,0 +1,10 @@
+namespace WebUI.Common.Services;
+
+public class SydneyTimeProvider : TimeProvider
+{
+ private const string Timezone = "AUS Eastern Standard Time";
+
+ private readonly TimeZoneInfo _sydneyTimeZone = TimeZoneInfo.FindSystemTimeZoneById(Timezone);
+
+ public override TimeZoneInfo LocalTimeZone => _sydneyTimeZone;
+}
diff --git a/src/WebUI/Common/Services/TimeProviderExt.cs b/src/WebUI/Common/Services/TimeProviderExt.cs
new file mode 100644
index 0000000..15aee46
--- /dev/null
+++ b/src/WebUI/Common/Services/TimeProviderExt.cs
@@ -0,0 +1,36 @@
+namespace WebUI.Common.Services;
+
+public static class TimeProviderExt
+{
+ public static DateOnly GetToday(this TimeProvider timeProvider)
+ {
+ var now = timeProvider.GetLocalNow();
+ return DateOnly.FromDateTime(now.Date);
+ }
+
+ public static DateTime GetStartOfDayUtc(this TimeProvider timeProvider, DateOnly date)
+ {
+ var timeZone = timeProvider.LocalTimeZone;
+
+ // Find the start of the day in Sydney time
+ var startOfDaySydney = date.ToDateTime(TimeOnly.MinValue);
+
+ // Convert the start of the day to UTC
+ var startOfDayUtc = TimeZoneInfo.ConvertTimeToUtc(startOfDaySydney, timeZone);
+
+ return startOfDayUtc;
+ }
+
+ public static DateTime GetEndOfDayUtc(this TimeProvider timeProvider, DateOnly date)
+ {
+ var timeZone = timeProvider.LocalTimeZone;
+
+ // Find the start of the day in Sydney time
+ var endOfDaySydney = date.ToDateTime(TimeOnly.MaxValue);
+
+ // Convert the start of the day to UTC
+ var endOfDayUtc = TimeZoneInfo.ConvertTimeToUtc(endOfDaySydney, timeZone);
+
+ return endOfDayUtc;
+ }
+}
\ No newline at end of file
diff --git a/src/WebUI/Components/Pages/Weather.razor b/src/WebUI/Components/Pages/Weather.razor
deleted file mode 100644
index 570e371..0000000
--- a/src/WebUI/Components/Pages/Weather.razor
+++ /dev/null
@@ -1,66 +0,0 @@
-@page "/weather"
-
-Weather
-
-
Weather
-
-This component demonstrates showing data.
-
-@if (forecasts == null)
-{
-
- Loading...
-
-}
-else
-{
-
-
-
- Date |
- Temp. (C) |
- Temp. (F) |
- Summary |
-
-
-
- @foreach (var forecast in forecasts)
- {
-
- @forecast.Date.ToShortDateString() |
- @forecast.TemperatureC |
- @forecast.TemperatureF |
- @forecast.Summary |
-
- }
-
-
-}
-
-@code {
- private WeatherForecast[]? forecasts;
-
- protected override async Task OnInitializedAsync()
- {
- // Simulate asynchronous loading to demonstrate a loading indicator
- await Task.Delay(500);
-
- var startDate = DateOnly.FromDateTime(DateTime.Now);
- var summaries = new[] { "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching" };
- forecasts = Enumerable.Range(1, 5).Select(index => new WeatherForecast
- {
- Date = startDate.AddDays(index),
- TemperatureC = Random.Shared.Next(-20, 55),
- Summary = summaries[Random.Shared.Next(summaries.Length)]
- }).ToArray();
- }
-
- private class WeatherForecast
- {
- public DateOnly Date { get; set; }
- public int TemperatureC { get; set; }
- public string? Summary { get; set; }
- public int TemperatureF => 32 + (int)(TemperatureC / 0.5556);
- }
-
-}
\ No newline at end of file
diff --git a/src/WebUI/Features/DailyScrum/Components/Pages/Debug.razor b/src/WebUI/Features/DailyScrum/Components/Pages/Debug.razor
new file mode 100644
index 0000000..1a96c3f
--- /dev/null
+++ b/src/WebUI/Features/DailyScrum/Components/Pages/Debug.razor
@@ -0,0 +1,19 @@
+@page "/daily-scrum/debug"
+@using Microsoft.AspNetCore.Components.Web
+@inject TimeProvider TimeProvider
+
+Daily Scrum
+
+Daily Scrum - Debug
+
+Now - @_now
+
+@code {
+ private DateTimeOffset _now;
+
+ protected override void OnInitialized()
+ {
+ _now = TimeProvider.GetLocalNow();
+ }
+
+}
diff --git a/src/WebUI/Features/DailyScrum/Components/Pages/Index.razor b/src/WebUI/Features/DailyScrum/Components/Pages/Index.razor
index 2abeda7..beaee48 100644
--- a/src/WebUI/Features/DailyScrum/Components/Pages/Index.razor
+++ b/src/WebUI/Features/DailyScrum/Components/Pages/Index.razor
@@ -2,7 +2,9 @@
@using Microsoft.AspNetCore.Components.Web
@using Microsoft.AspNetCore.Components.Forms
@using System.ComponentModel.DataAnnotations
+@using WebUI.Common.Services
@inject NavigationManager NavigationManager
+@inject TimeProvider TimeProvider
Daily Scrum
@@ -54,6 +56,12 @@
// https://github.com/dotnet/aspnetcore/issues/52195
public string ClientDays { get; set; } = String.Empty;
- public DateOnly LastWorkingDay { get; set; } = DateOnly.FromDateTime(DateTime.Now.AddDays(-1));
+ public DateOnly LastWorkingDay { get; set; }
+ }
+
+ protected override void OnInitialized()
+ {
+ if (Model.LastWorkingDay == default)
+ Model.LastWorkingDay = TimeProvider.GetToday().AddDays(-1);
}
}
diff --git a/src/WebUI/Features/DailyScrum/DailyScrumFeature.cs b/src/WebUI/Features/DailyScrum/DailyScrumFeature.cs
index a0f3e29..e487626 100644
--- a/src/WebUI/Features/DailyScrum/DailyScrumFeature.cs
+++ b/src/WebUI/Features/DailyScrum/DailyScrumFeature.cs
@@ -1,4 +1,5 @@
using WebUI.Common.Features;
+using WebUI.Common.Services;
using WebUI.Features.DailyScrum.Infrastructure;
namespace WebUI.Features.DailyScrum;
@@ -10,5 +11,6 @@ public static void ConfigureServices(IServiceCollection services, IConfiguration
//services.AddOptionsWithValidation(MicrosoftGraphOptions.Section);
//services.AddScoped();
services.AddScoped();
+ services.AddScoped();
}
}
diff --git a/src/WebUI/Features/DailyScrum/Infrastructure/GraphService.cs b/src/WebUI/Features/DailyScrum/Infrastructure/GraphService.cs
index aa1da87..e5021bf 100644
--- a/src/WebUI/Features/DailyScrum/Infrastructure/GraphService.cs
+++ b/src/WebUI/Features/DailyScrum/Infrastructure/GraphService.cs
@@ -15,10 +15,12 @@ public interface IGraphService
public class GraphService : IGraphService
{
private readonly ICurrentUserService _currentUserService;
+ private readonly ILogger _logger;
- public GraphService(ICurrentUserService currentUserService)
+ public GraphService(ICurrentUserService currentUserService, ILogger logger)
{
_currentUserService = currentUserService;
+ _logger = logger;
}
// public async Task?> GetTodoLists()
@@ -46,6 +48,8 @@ public GraphService(ICurrentUserService currentUserService)
public async Task> GetTasks(DateTime utcStart, DateTime utcEnd)
{
+ _logger.LogInformation("Getting tasks from {UtcStart} to {UtcEnd}", utcStart, utcEnd);
+
var graphClient = GetGraphServiceClient();
// NOTE: SHOULD be able to use OData to expand the child tasks, but I haven't been able to get this to work
@@ -55,7 +59,6 @@ public async Task> GetTasks(DateTime utcStart, DateTime utcEnd)
var tasks = new Dictionary>();
-
foreach (var list in lists.Value)
{
var task = graphClient.Me.Todo
diff --git a/src/WebUI/Features/DailyScrum/Queries/GetDailyScrumQuery.cs b/src/WebUI/Features/DailyScrum/Queries/GetDailyScrumQuery.cs
index b076b9d..d8af2a1 100644
--- a/src/WebUI/Features/DailyScrum/Queries/GetDailyScrumQuery.cs
+++ b/src/WebUI/Features/DailyScrum/Queries/GetDailyScrumQuery.cs
@@ -1,4 +1,5 @@
using MediatR;
+using WebUI.Common.Services;
using WebUI.Common.ViewModels;
using WebUI.Features.DailyScrum.Infrastructure;
@@ -10,10 +11,14 @@ public record GetDailyScrumQuery(string Name, int? ClientDays, DateOnly? LastWor
public class GetDailyScrumQueryHandler : IRequestHandler
{
private readonly IGraphService _graphService;
+ private readonly TimeProvider _timeProvider;
+ private readonly ILogger _logger;
- public GetDailyScrumQueryHandler(IGraphService graphService)
+ public GetDailyScrumQueryHandler(IGraphService graphService, TimeProvider timeProvider, ILogger logger)
{
_graphService = graphService;
+ _timeProvider = timeProvider;
+ _logger = logger;
}
public async Task Handle(GetDailyScrumQuery request, CancellationToken cancellationToken)
@@ -22,7 +27,9 @@ public async Task Handle(GetDailyScrumQuery request, Cancel
var userSummary = await GetUserSummary(request.ClientDays);
- var today = GetToday();
+ var today = _timeProvider.GetToday();
+ _logger.LogInformation("Getting projects for {Today}", today);
+
var todaysProjects = await GetProjects(today);
var yesterday = GetLastWorkingDay(request.LastWorkingDay);
@@ -52,7 +59,11 @@ private async Task GetUserSummary(int? clientDays)
// TODO: Consider refactoring into a common service
private async Task> GetProjects(DateOnly date)
{
- var (startOfDayUtc, endOfDayUtc) = GetTimeStamps(date);
+ // TODO: This is not returning the correct timestamps on local vs docker
+ var startOfDayUtc = _timeProvider.GetStartOfDayUtc(date);
+ var endOfDayUtc = _timeProvider.GetEndOfDayUtc(date);
+
+ _logger.LogInformation("Getting projects for {Date} ({StartOfDayUtc} to {EndOfDayUtc})", date, startOfDayUtc, endOfDayUtc);
var graphTasks = await _graphService.GetTasks(startOfDayUtc, endOfDayUtc);
@@ -91,24 +102,6 @@ private EmailViewModel GetEmail(string name)
};
}
- // TODO: Consider refactoring into a common service
- private (DateTime StartOfDayUtc, DateTime EndOfDayUtc) GetTimeStamps(DateOnly localDate)
- {
- // Find the start of the day
- var startOfDayLocal = localDate.ToDateTime(TimeOnly.MinValue);
-
- // Find the end of the day
- var endOfDayLocal = localDate.ToDateTime(TimeOnly.MaxValue);
-
- // Convert to UTC
- var startOfDayUtc = startOfDayLocal.ToUniversalTime();
- var endOfDayUtc = endOfDayLocal.ToUniversalTime();
-
- return (startOfDayUtc, endOfDayUtc);
- }
-
- private DateOnly GetToday() => DateOnly.FromDateTime(DateTime.Now);
-
private DateOnly GetLastWorkingDay(DateOnly? lastWorkingDay) =>
- lastWorkingDay ?? DateOnly.FromDateTime(DateTime.Now.AddDays(-1));
+ lastWorkingDay ?? _timeProvider.GetToday().AddDays(-1);
}
diff --git a/src/WebUI/Features/Timesheet/Components/Pages/Index.razor b/src/WebUI/Features/Timesheet/Components/Pages/Index.razor
index 61df402..aa6095a 100644
--- a/src/WebUI/Features/Timesheet/Components/Pages/Index.razor
+++ b/src/WebUI/Features/Timesheet/Components/Pages/Index.razor
@@ -2,7 +2,9 @@
@using Microsoft.AspNetCore.Components.Web
@using Microsoft.AspNetCore.Components.Forms
@using System.ComponentModel.DataAnnotations
+@using WebUI.Common.Services
@inject NavigationManager NavigationManager
+@inject TimeProvider TimeProvider
Timesheet Notes
@@ -37,6 +39,13 @@
public class InputModel
{
[Required]
- public DateOnly Date { get; set; } = DateOnly.FromDateTime(DateTime.Now);
+ public DateOnly Date { get; set; }
}
+
+ protected override void OnInitialized()
+ {
+ if (Model.Date == default)
+ Model.Date = TimeProvider.GetToday();
+ }
+
}
diff --git a/src/WebUI/Features/Timesheet/Queries/GetTimeSheetNotesQuery.cs b/src/WebUI/Features/Timesheet/Queries/GetTimeSheetNotesQuery.cs
index 91e4eea..5fd4952 100644
--- a/src/WebUI/Features/Timesheet/Queries/GetTimeSheetNotesQuery.cs
+++ b/src/WebUI/Features/Timesheet/Queries/GetTimeSheetNotesQuery.cs
@@ -1,23 +1,34 @@
using MediatR;
+using WebUI.Common.Services;
using WebUI.Common.ViewModels;
using WebUI.Features.DailyScrum.Infrastructure;
using WebUI.Features.DailyScrum.Queries;
namespace WebUI.Features.Timesheet.Queries;
+// How should the time calculation work?
+// - user enters a date
+// - we assume that that date is in Sydney time
+//
public record GetTimeSheetNotesQuery(DateOnly Date) : IRequest;
public class GetTimeSheetNotesQueryHandler : IRequestHandler
{
private readonly IGraphService _graphService;
+ private readonly TimeProvider _timeProvider;
+ private readonly ILogger _logger;
- public GetTimeSheetNotesQueryHandler(IGraphService graphService)
+ public GetTimeSheetNotesQueryHandler(IGraphService graphService, TimeProvider timeProvider, ILogger logger)
{
_graphService = graphService;
+ _timeProvider = timeProvider;
+ _logger = logger;
}
public async Task Handle(GetTimeSheetNotesQuery request, CancellationToken cancellationToken)
{
+ _logger.LogInformation("Getting timesheet notes for {Date}", request.Date);
+
var projects = await GetProjects(request.Date);
return new TimesheetViewModel
@@ -29,7 +40,10 @@ public async Task Handle(GetTimeSheetNotesQuery request, Can
// TODO: Consider refactoring into a common service
private async Task> GetProjects(DateOnly date)
{
- var (startOfDayUtc, endOfDayUtc) = GetTimeStamps(date);
+ var startOfDayUtc = _timeProvider.GetStartOfDayUtc(date);
+ var endOfDayUtc = _timeProvider.GetEndOfDayUtc(date);
+
+ _logger.LogInformation("Getting projects for {Date} ({StartOfDayUtc} to {EndOfDayUtc})", date, startOfDayUtc, endOfDayUtc);
var graphTasks = await _graphService.GetTasks(startOfDayUtc, endOfDayUtc);
@@ -46,20 +60,4 @@ private async Task> GetProjects(DateOnly date)
return projects;
}
-
- // TODO: Consider refactoring into a common service
- private (DateTime StartOfDayUtc, DateTime EndOfDayUtc) GetTimeStamps(DateOnly localDate)
- {
- // Find the start of the day
- var startOfDayLocal = localDate.ToDateTime(TimeOnly.MinValue);
-
- // Find the end of the day
- var endOfDayLocal = localDate.ToDateTime(TimeOnly.MaxValue);
-
- // Convert to UTC
- var startOfDayUtc = startOfDayLocal.ToUniversalTime();
- var endOfDayUtc = endOfDayLocal.ToUniversalTime();
-
- return (startOfDayUtc, endOfDayUtc);
- }
}
diff --git a/tests/IntegrationTests/GraphServiceTests.cs b/tests/IntegrationTests/GraphServiceTests.cs
index 4137135..9bd2b15 100644
--- a/tests/IntegrationTests/GraphServiceTests.cs
+++ b/tests/IntegrationTests/GraphServiceTests.cs
@@ -1,6 +1,8 @@
using FluentAssertions;
using Microsoft.Extensions.Configuration;
+using Microsoft.Extensions.Logging;
using WebUI.Common.Identity;
+using WebUI.Common.Services;
using WebUI.Features.DailyScrum.Infrastructure;
namespace IntegrationTests;
@@ -24,7 +26,8 @@ public async Task CanGetTodaysTasks()
{
// Arrange
var sut = CreateGraphService();
- var today = DateOnly.FromDateTime(DateTime.Now);
+ var timeProvider = new SydneyTimeProvider();
+ var today = timeProvider.GetToday();
var startOfDayLocal = today.ToDateTime(TimeOnly.MinValue);
var endOfDayLocal = today.ToDateTime(TimeOnly.MaxValue);
var startOfDayUtc = startOfDayLocal.ToUniversalTime();
@@ -51,7 +54,9 @@ private GraphService CreateGraphService()
{
var userService = new CurrentUserService();
userService.UpdateAccessToken(_accessToken);
- var service = new GraphService(userService);
+ var timeProvider = new SydneyTimeProvider();
+ var logger = new Logger(new LoggerFactory());
+ var service = new GraphService(userService, logger);
return service;
}
}
diff --git a/tests/UnitTests/GlobalUsings.cs b/tests/UnitTests/GlobalUsings.cs
new file mode 100644
index 0000000..8c927eb
--- /dev/null
+++ b/tests/UnitTests/GlobalUsings.cs
@@ -0,0 +1 @@
+global using Xunit;
\ No newline at end of file
diff --git a/tests/UnitTests/SydneyTimeProviderTests.cs b/tests/UnitTests/SydneyTimeProviderTests.cs
new file mode 100644
index 0000000..4f49781
--- /dev/null
+++ b/tests/UnitTests/SydneyTimeProviderTests.cs
@@ -0,0 +1,16 @@
+using FluentAssertions;
+using WebUI.Common.Services;
+
+namespace UnitTests;
+
+public class SydneyTimeProviderTests
+{
+ [Fact]
+ public void GetLocalNow_ReturnsSydneyTime()
+ {
+ var sut = new SydneyTimeProvider();
+ var localNow = DateTimeOffset.Now;
+ var result = sut.GetLocalNow();
+ result.Should().BeCloseTo(localNow, precision: TimeSpan.FromSeconds(5));
+ }
+}
diff --git a/tests/UnitTests/TimeProviderExtTests.cs b/tests/UnitTests/TimeProviderExtTests.cs
new file mode 100644
index 0000000..72cfa2c
--- /dev/null
+++ b/tests/UnitTests/TimeProviderExtTests.cs
@@ -0,0 +1,37 @@
+using FluentAssertions;
+using WebUI.Common.Services;
+
+namespace UnitTests;
+
+public class TimeProviderExtTests
+{
+ [Fact]
+ public void GetStartOfDayUtc_GivenSydneyTimeZone_ReturnsCorrectUtc()
+ {
+ var sut = new SydneyTimeProvider();
+ var date = new DateOnly(2024, 5, 5);
+ var expected = new DateTime(2024, 5, 4, 14, 0, 0, DateTimeKind.Utc);
+
+ var result = sut.GetStartOfDayUtc(date);
+
+ result.Should().Be(expected);
+ }
+
+ [Fact]
+ public void GetEndOfDayUtc_GivenSydneyTimeZone_ReturnsCorrectUtc()
+ {
+ var sut = new SydneyTimeProvider();
+ var date = new DateOnly(2024, 5, 5);
+ var expected = new DateTime(2024, 5, 5, 13, 59, 59, DateTimeKind.Utc);
+
+ var result = sut.GetEndOfDayUtc(date);
+
+ result.Should().BeCloseTo(expected, TimeSpan.FromSeconds(1));
+ }
+
+
+
+
+
+
+}
\ No newline at end of file
diff --git a/tests/UnitTests/UnitTests.csproj b/tests/UnitTests/UnitTests.csproj
new file mode 100644
index 0000000..ec27f03
--- /dev/null
+++ b/tests/UnitTests/UnitTests.csproj
@@ -0,0 +1,30 @@
+
+
+
+ net8.0
+ enable
+ enable
+
+ false
+ true
+
+
+
+
+
+
+
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+ all
+
+
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+ all
+
+
+
+
+
+
+
+