Skip to content

Commit

Permalink
Use local time offset to ensure DST is applied correctly for AB#16861.
Browse files Browse the repository at this point in the history
  • Loading branch information
BrianMaki committed Nov 6, 2024
1 parent ddb1d8d commit c0fb17e
Show file tree
Hide file tree
Showing 3 changed files with 27 additions and 29 deletions.
7 changes: 3 additions & 4 deletions Apps/Admin/Server/Services/AdminServerMappingService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,6 @@ namespace HealthGateway.Admin.Server.Services
/// <inheritdoc/>
public class AdminServerMappingService(IMapper mapper, IConfiguration configuration) : IAdminServerMappingService
{
private TimeZoneInfo LocalTimeZone => DateFormatter.GetLocalTimeZone(configuration);

/// <inheritdoc/>
public AdminTag MapToAdminTag(AdminTagView source)
{
Expand All @@ -54,9 +52,10 @@ public AdminUserProfileView MapToAdminUserProfileView(AdminUserProfile source)
{
AdminUserProfileView dest = mapper.Map<AdminUserProfile, AdminUserProfileView>(source);

// change from UTC to local time, potentially resulting in Kind = DateTimeKind.Unspecified
// change from UTC to local time using local time offset to ensure daylight savings time is applied correctly.
// note that this model is not returned in any API calls; it is used to populate a CSV with values in local time
dest.LastLoginDateTime = TimeZoneInfo.ConvertTimeFromUtc(source.LastLoginDateTime, this.LocalTimeZone);
TimeSpan localTimeOffset = DateFormatter.GetLocalTimeOffset(configuration, DateTime.UtcNow);
dest.LastLoginDateTime = source.LastLoginDateTime.AddMinutes(localTimeOffset.TotalMinutes);

return dest;
}
Expand Down
8 changes: 5 additions & 3 deletions Apps/Admin/Tests/Services/CsvExportServiceTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -277,6 +277,8 @@ public async Task ShouldGetYearOfBirthCounts()
[Fact]
public void TestMapperAdminUserProfileViewFromAdminUserProfile()
{
TimeSpan localTimeOffset = DateFormatter.GetLocalTimeOffset(Configuration, DateTime.UtcNow);

AdminUserProfile adminUserProfile = new()
{
AdminUserProfileId = Guid.NewGuid(),
Expand All @@ -288,7 +290,7 @@ public void TestMapperAdminUserProfileViewFromAdminUserProfile()
{
AdminUserProfileId = adminUserProfile.AdminUserProfileId,
UserId = null,
LastLoginDateTime = adminUserProfile.LastLoginDateTime,
LastLoginDateTime = adminUserProfile.LastLoginDateTime.AddMinutes(localTimeOffset.TotalMinutes),
Username = adminUserProfile.Username,
Email = null,
FirstName = null,
Expand All @@ -301,8 +303,8 @@ public void TestMapperAdminUserProfileViewFromAdminUserProfile()
Assert.Equal(expected.AdminUserProfileId, actual.AdminUserProfileId);
Assert.Equal(expected.UserId, actual.UserId);
Assert.NotNull(actual.LastLoginDateTime);
Assert.Equal(DateTimeKind.Unspecified, actual.LastLoginDateTime.Value.Kind);
Assert.Equal(expected.LastLoginDateTime, DateFormatter.SpecifyLocal(actual.LastLoginDateTime.Value, Configuration));
Assert.Equal(DateTimeKind.Utc, actual.LastLoginDateTime.Value.Kind);
Assert.Equal(expected.LastLoginDateTime, actual.LastLoginDateTime.Value);
Assert.Equal(expected.Username, actual.Username);
Assert.Equal(expected.Email, actual.Email);
Assert.Equal(expected.FirstName, actual.FirstName);
Expand Down
41 changes: 19 additions & 22 deletions Apps/Admin/Tests/Services/InactiveUserServiceTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,18 @@ public async Task ShouldGetInactiveUsers(bool adminUserErrorExists, bool support
// Arrange
const int inactiveDays = 3;
DateTime today = DateTime.UtcNow.Date;
IList<AdminUserProfile> inactiveUserProfiles = GenerateInactiveUserProfiles(today);
IList<AdminUserProfile> activeUserProfiles =
[
GenerateAdminUserProfile(AdminUsername1, today.AddDays(-1)),
GenerateAdminUserProfile(SupportUsername1, today.AddDays(-2)),
];

IList<AdminUserProfile> inactiveUserProfiles =
[
GenerateAdminUserProfile(AdminUsername2, today.AddDays(-3)),
GenerateAdminUserProfile(SupportUsername2, today.AddDays(-4)),
];

List<UserRepresentation> keycloakAdminUsers = GenerateKeycloakAdminUsers();
List<UserRepresentation> keycloakSupportUsers = GenerateKeycloakSupportUsers();
TimeSpan localTimeOffset = DateFormatter.GetLocalTimeOffset(Configuration, DateTime.UtcNow);
Expand All @@ -97,7 +108,7 @@ public async Task ShouldGetInactiveUsers(bool adminUserErrorExists, bool support
: null,
};

IInactiveUserService service = await SetupGetInactiveUsersMock(today, adminUserErrorExists, supportUserErrorExists);
IInactiveUserService service = await SetupInactiveUsersMock(inactiveDays, activeUserProfiles, inactiveUserProfiles, adminUserErrorExists, supportUserErrorExists);

// Act
RequestResult<List<AdminUserProfileView>> actual = await service.GetInactiveUsersAsync(inactiveDays);
Expand Down Expand Up @@ -201,15 +212,6 @@ private static List<UserRepresentation> GenerateKeycloakSupportUsers()
];
}

private static IList<AdminUserProfile> GenerateInactiveUserProfiles(DateTime today)
{
return
[
GenerateAdminUserProfile(AdminUsername2, today.AddDays(-3)),
GenerateAdminUserProfile(SupportUsername2, today.AddDays(-4)),
];
}

private static UserRepresentation GenerateUserRepresentation(Guid userId, string username, string firstName, string lastName, string email, string roles)
{
return new()
Expand All @@ -236,20 +238,15 @@ private static IConfigurationRoot GetIConfigurationRoot()
.Build();
}

private static async Task<IInactiveUserService> SetupGetInactiveUsersMock(DateTime today, bool adminUserErrorExists, bool supportUserErrorExists)
private static async Task<IInactiveUserService> SetupInactiveUsersMock(
int inactiveDays,
IList<AdminUserProfile> activeUserProfiles,
IList<AdminUserProfile> inactiveUserProfiles,
bool adminUserErrorExists,
bool supportUserErrorExists)
{
const int inactiveDays = 3;

JwtModel jwt = GenerateJwt();

IList<AdminUserProfile> activeUserProfiles =
[
GenerateAdminUserProfile(AdminUsername1, today.AddDays(-1)),
GenerateAdminUserProfile(SupportUsername1, today.AddDays(-2)),
];
IList<AdminUserProfile> inactiveUserProfiles = GenerateInactiveUserProfiles(today);
List<UserRepresentation> keycloakAdminUsers = GenerateKeycloakAdminUsers();

List<UserRepresentation> keycloakSupportUsers = GenerateKeycloakSupportUsers();
Expand Down

0 comments on commit c0fb17e

Please sign in to comment.