According to MSDN ,UserPrinciple
和 ComputerPrinciple
类的查找方法(如 FindByLogonTime 和 FindByBadPasswordAttempt)使用 UTC 时间来筛选结果。但是,测试表明这些方法似乎将本地时间作为输入,而生成的对象使用 UTC 作为其时间属性,并且需要转换为本地时间。
下面的示例似乎正确列出了自本地时间早上 6 点以来在服务器上尝试登录失败的所有用户:
static void Main()
{
var pc = new PrincipalContext(ContextType.Domain, dcServer);
PrincipalSearchResult<UserPrincipal> uFailed = UserPrincipal.FindByBadPasswordAttempt(pc, DateTime.Now.Date.AddHours(6), MatchType.GreaterThan);
string s = "";
foreach (UserPrincipal u in uFailed)
{
s += u.SamAccountName + ": " + Convert.ToDateTime(u.LastBadPasswordAttempt).ToLocalTime().ToString() + "\r\n";
}
Console.Write(s);
Console.Read();
}
请注意 LastBadPasswordAttempt 属性的 UTC 转换,但 FindByBadPasswordAttempt 过滤器中没有转换。
在不知道我是否遗漏了某些内容(可能)或者 MSDN 文档是否错误的情况下,我对将其投入生产感到紧张。该代码在其查询的 DC 上运行,因此应该不存在时区问题。
最佳答案
MSDN 文档是准确的,您只需要注意传入的 DateTime
值的 .Kind
属性即可。
如果您深入研究引用源或反汇编,您会发现您传递的 DateTime
值最终会传递到 DateTime.ToFileTimeUtc
,它确实在其行为中考虑了 Kind
。
因此,您可以传入一个基于 DateTime.UtcNow
的值,该值的 .Kind
为 DateTimeKind.Utc
并且它将起作用,或者您可以像当前正在做的那样传递 DateTime.Now
,它的 .Kind
为 DateTimeKind.Local
并且本地时间将在查询之前将其转换为 UTC。只需确保您认识到这是运行代码的计算机的本地时间。如果这有可能与您的用户不在同一时区,那么您可能需要使用 TimeZoneInfo
来获取与其他本地时区等效的 UTC 时间。
如果由于某种原因您决定传入您自己构建的日期,它可能会有 DateTimeKind.Unspecified
。对于此特定函数,将被视为 UTC。这并不一定适用于所有日期/时间函数,因此请小心。
此外,在输出中,当您使用的值已经是 DateTime
时,无需调用 Convert.ToDateTime
。就此而言,您甚至不需要此代码中的 ToString
,除非您计划传递格式说明符。再次强调,如果您的用户可能位于其他时区,请不要使用 .ToLocalTime
,而是使用 TimeZoneInfo
上的方法之一进行转换。
另一个微小的优化,如果需要,您可以使用 DateTime.Today
而不是 DateTime.Now.Date
。 (除了可读性稍微好一点之外,没有什么区别。)
关于.NET DirectoryServices 查找方法使用 UTC 还是本地时间?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/19599870/