获取计算机最后登录时间遇Ticks差异问题及正确处理方法求助
我需要从两台域控制器(DC)获取计算机最后登录时间,筛选出自2022年5月1日起未登录的计算机。已将第一台DC的计算机对象(含lastLogon、lastLogontimeStamp属性)存入$KolStacji23变量,第二台DC的存入$KolStacji71变量,随后对比这些属性值。
我的代码如下:
$Nieaktywne = ((Get-Date).AddDays(- 120)).Ticks foreach ($Stacja23 in $KolStacji23) { foreach ($Stacja71 in $KolStacji71) { if ($Stacja23.Name -eq $Stacja71.Name) { if ((($($Stacja23.LastLogon) -lt $Nieaktywne) -and ($($Stacja71.LastLogon) -lt $Nieaktywne) -and ($($Stacja23.lastLogontimeStamp) -lt $Nieaktywne)) -and ($Stacja23.whenCreated -lt $Nieaktywne)) { $Stacja23.Name if ($Stacja23.lastLogontimeStamp -gt $Stacja23.LastLogon) { $Stacja23.LastLogon = $Stacja23.lastLogontimeStamp } if ($Stacja71.LastLogon -gt $Stacja23.LastLogon) { $Stacja23.LastLogon = $Stacja71.LastLogon } $Stacja23 | Select-Object Name, @{ N = 'lokalizacja'; E = { ($($_.CanonicalName)).split("/")[-3] } }, whenCreated, @{ N = 'LastLogon'; E = { [DateTime]::FromFileTime($_.LastLogon) } }, operatingSystem, Description | Export-Csv -Path $PlikListy -Delimiter ';' -Force -NoClobber -Encoding UTF8 -Append -NoTypeInformation } Break } } }
系统为波兰语,其中“kwietnia”指4月,“sierpnia”指8月,“września”指9月。执行命令发现系统Get-Date的Ticks值与计算机账户属性的Ticks值差异极大:
PS C:\Windows\system32> Get-Date czwartek, 1 września 2022 13:49:57 PS C:\Windows\system32> (Get-Date).AddDays(- 124) sobota, 30 kwietnia 2022 13:50:06 PS C:\Windows\system32> ((Get-Date).AddDays(- 124)).Ticks 637869234152030929
而计算机账户属性的Ticks值及转换结果如下:
([DateTime]::FromFileTime($Stacja23.LastLogon)) środa, 22 września 2021 15:48:59 $Stacja23.LastLogon 132767921394245654 [DateTime]::FromFileTime($Stacja23.LastLogontimeStamp) poniedziałek, 22 sierpnia 2022 19:05:11 $Stacja23.LastLogontimeStamp 133056615112570357
若直接使用[datetime]转换这些属性值,会得到公元400多年的远古日期:
$Stacja23.LastLogontimeStamp 133056615112570357 [datetime]$Stacja23.LastLogon środa, 22 września 0421 13:48:59 [datetime]$Stacja23.LastLogontimeStamp poniedziałek, 22 sierpnia 0422 17:05:11
请问计算机账户的这些属性的Ticks计算方式是否不同?该如何正确处理?是否需要添加对应1600年的Ticks值?具体数值是多少?
1. 时间格式差异原因
AD中lastLogon和lastLogontimeStamp属性使用的是Windows文件时间格式,和.NET DateTime.Ticks的计数起点完全不同:
- Windows文件时间:从1601年1月1日UTC开始,以100纳秒为间隔计数
- .NET
DateTime.Ticks:从0001年1月1日开始,以100纳秒为间隔计数
这就是直接用[datetime]转换AD属性值会得到远古日期的原因——两者的起始时间差了1600年左右。
2. 正确处理方式
不需要手动添加1600年的Ticks值,直接使用专门的转换方法和格式匹配的阈值即可:
步骤1:生成格式匹配的阈值时间
把筛选阈值(2022年5月1日)转换成Windows文件时间格式,用来和AD属性值直接比较:
# 定义2022年5月1日为阈值,转换为UTC的Windows文件时间 $Nieaktywne = (Get-Date "2022-05-01").ToUniversalTime().ToFileTime()
注:AD时间属性存储的是UTC时间,所以转换阈值时要统一为UTC,避免时区偏差
步骤2:修正代码中的比较逻辑
直接用转换后的Windows文件时间阈值和AD属性值比较,不需要用.NET的Ticks:
修正后的核心代码片段:
$Nieaktywne = (Get-Date "2022-05-01").ToUniversalTime().ToFileTime() $thresholdDate = Get-Date "2022-05-01" foreach ($Stacja23 in $KolStacji23) { foreach ($Stacja71 in $KolStacji71) { if ($Stacja23.Name -eq $Stacja71.Name) { # 比较AD属性的Windows文件时间与阈值 $isInactive = ($Stacja23.LastLogon -lt $Nieaktywne) -and ($Stacja71.LastLogon -lt $Nieaktywne) -and ($Stacja23.lastLogontimeStamp -lt $Nieaktywne) -and # whenCreated是DateTime类型,直接和日期对象比较 ($Stacja23.whenCreated -lt $thresholdDate) if ($isInactive) { # 取两个DC和lastLogontimeStamp中的最大时间作为最后登录时间 $maxLogonTime = [Math]::Max($Stacja23.LastLogon, $Stacja71.LastLogon) $maxLogonTime = [Math]::Max($maxLogonTime, $Stacja23.lastLogontimeStamp) $Stacja23 | Select-Object Name, @{ N = 'lokalizacja'; E = { $_.CanonicalName.Split("/")[-3] } }, whenCreated, @{ N = 'LastLogon'; E = { [DateTime]::FromFileTime($maxLogonTime) } }, operatingSystem, Description | Export-Csv -Path $PlikListy -Delimiter ';' -Force -NoClobber -Encoding UTF8 -Append -NoTypeInformation } Break } } }
3. 额外说明
lastLogon:仅存储在用户/计算机登录的那台DC上,不会同步到其他DC,所以需要从多台DC收集后取最大值lastLogontimeStamp:会在域中同步,但默认最多14天更新一次,精度不如lastLogon,但适合跨DC获取大致的登录时间
内容的提问来源于stack exchange,提问作者KrzychG

