@@ -129,7 +129,7 @@ private void Map(IUser source, BackOfficeIdentityUser target)
129129 target . IsApproved = source . IsApproved ;
130130 target . SecurityStamp = source . SecurityStamp ;
131131 DateTime ? lockedOutUntil = source . LastLockoutDate ? . AddMinutes ( _securitySettings . UserDefaultLockoutTimeInMinutes ) ;
132- target . LockoutEnd = source . IsLockedOut ? ( lockedOutUntil ?? DateTime . MaxValue ) . ToUniversalTime ( ) : null ;
132+ target . LockoutEnd = source . IsLockedOut ? lockedOutUntil ?? DateTime . MaxValue : null ;
133133 }
134134
135135 // Umbraco.Code.MapAll -Id -LockoutEnabled -PhoneNumber -PhoneNumberConfirmed -ConcurrencyStamp -NormalizedEmail -NormalizedUserName -Roles
@@ -146,17 +146,46 @@ private void Map(IMember source, MemberIdentityUser target)
146146 target . PasswordConfig = source . PasswordConfiguration ;
147147 target . IsApproved = source . IsApproved ;
148148 target . SecurityStamp = source . SecurityStamp ;
149- DateTime ? lockedOutUntil = source . LastLockoutDate ? . AddMinutes ( _securitySettings . MemberDefaultLockoutTimeInMinutes ) ;
150- target . LockoutEnd = source . IsLockedOut ? ( lockedOutUntil ?? DateTime . MaxValue ) . ToUniversalTime ( ) : null ;
149+ target . LockoutEnd = GetLockoutEnd ( source ) ;
150+ target . LastLockoutDateUtc = GetLastLockoutDateUtc ( source ) ;
151151 target . Comments = source . Comments ;
152- target . LastLockoutDateUtc = source . LastLockoutDate == DateTime . MinValue
153- ? null
154- : source . LastLockoutDate ? . ToUniversalTime ( ) ;
155- target . CreatedDateUtc = source . CreateDate . ToUniversalTime ( ) ;
152+ target . CreatedDateUtc = EnsureUtcWithServerTime ( source . CreateDate ) ;
156153 target . Key = source . Key ;
157154 target . MemberTypeAlias = source . ContentTypeAlias ;
158155 target . TwoFactorEnabled = _twoFactorLoginService . IsTwoFactorEnabledAsync ( source . Key ) . GetAwaiter ( ) . GetResult ( ) ;
159156
160157 // NB: same comments re AutoMapper as per BackOfficeUser
161158 }
159+
160+ private DateTimeOffset ? GetLockoutEnd ( IMember source )
161+ {
162+ if ( source . IsLockedOut is false )
163+ {
164+ return null ;
165+ }
166+
167+ DateTime ? lockedOutUntil = source . LastLockoutDate ? . AddMinutes ( _securitySettings . MemberDefaultLockoutTimeInMinutes ) ;
168+ if ( lockedOutUntil . HasValue is false )
169+ {
170+ return DateTime . MaxValue ;
171+ }
172+
173+ return EnsureUtcWithServerTime ( lockedOutUntil . Value ) ;
174+ }
175+
176+ private static DateTime ? GetLastLockoutDateUtc ( IMember source )
177+ {
178+ if ( source . LastLockoutDate is null || source . LastLockoutDate == DateTime . MinValue )
179+ {
180+ return null ;
181+ }
182+
183+ return EnsureUtcWithServerTime ( source . LastLockoutDate . Value ) ;
184+ }
185+
186+ private static DateTime EnsureUtcWithServerTime ( DateTime date ) =>
187+
188+ // We have a server time value here, but the the Kind is UTC, so we can't use .ToUniversalTime() to convert to the UTC
189+ // value that the LockoutEnd property expects. We need to create a DateTimeOffset with the correct offset.
190+ DateTime . SpecifyKind ( date , DateTimeKind . Local ) . ToUniversalTime ( ) ;
162191}
0 commit comments