我有一台 Windows 机器,它会定期更改系统时间,原因不明。它似乎每小时发生一次。
此 Windows 计算机是虚拟机(Parallels Desktop 9、Win7 客户机、OSX 主机)。它具有 NTP 服务(网络时间) 运行可以迅速纠正错误,但在改变和纠正之间的短短几秒钟内,它会导致问题。
我检查过:
- VM 时间同步已禁用
- Windows“Internet 时间”已禁用
- Windows 时间服务已禁用
- 我只运行一个 NTP 客户端,每 15 分钟更新一次
有一个复杂因素。我们运行夜间天文学服务。为了避免自动 DST 更改引起的问题,我们禁用自动 DST 更改,并在当天晚些时候手动将机器时区设置为具有正确偏移的时区。例如,在西班牙,现在是 DST。标准时间为 UTC+1,DST 为 UTC+2。DST 更改后的早晨,我们将机器时区设置为希腊 UTC+2。主机配置正常(正确的时区,自动 DST 更改)。复杂之处在于时钟在 UTC+1(DST 之前的时间)更改回当前时间。
某些进程正在改变这一点。可能它有自己的时区设置。但我无法追踪它。更改记录在系统日志中。有两个关键条目:时间设置错误的位置以及更正时间的时间:
(全面披露,事件日志按事件 ID = 1 进行过滤,但其他事件似乎毫无意义)。
有趣的是,这些事件发生的频率非常高(每小时,精确到秒)。更有趣的是,这些事件是正常运行时间。我可以在任务管理器中查看系统启动时间,当时间超过一个小时时,时钟就会发生变化。
查看事件详情也很有趣:
- <Event xmlns="http://schemas.microsoft.com/win/2004/08/events/event">
- <System>
<Provider Name="Microsoft-Windows-Kernel-General" Guid="{GUID}" />
<EventID>1</EventID>
<Version>0</Version>
<Level>4</Level>
<Task>0</Task>
<Opcode>0</Opcode>
<Keywords>0x8000000000000010</Keywords>
<TimeCreated SystemTime="2018-04-18T00:31:28.500000000Z" />
<EventRecordID>500706</EventRecordID>
<Correlation />
<Execution ProcessID="4" ThreadID="56" />
<Channel>System</Channel>
<Computer>T07-VM-GUEST</Computer>
<Security UserID="SID" />
</System>
- <EventData>
<Data Name="NewTime">2018-04-18T00:31:28.500000000Z</Data>
<Data Name="OldTime">2018-04-18T01:31:28.861800000Z</Data>
</EventData>
</Event>
我们可以看到此事件从 01:31 变为 00:31(UTC 时间,事件日志中显示的当地时间为 03:31 至 02:31)。特别有趣的是这一行:
<Execution ProcessID="4" ThreadID="56" />
使用 ProcessExplorer 我可以检查系统进程(PID 4),并且可以看到有关 ThreadId 56 的一些详细信息(假设它们没有被回收,而我正在查看正确的进程):
但对我来说,这都是胡言乱语。我在这里能看到的唯一有意义的东西是开始时间,以及它与时钟变化事件时间的关系(正如我上面所说,每小时与正常运行时间同步)。
这个答案讨论了在安全日志中查找时间更改,并且 NetTime 服务发起的所有更改都在那里。但有问题的更改可疑地缺失了:
我的分析正确吗?如果正确,为什么系统进程每小时都会更改我的系统时钟?
答案1
好吧,正如所有最严重类型的问题一样,这个问题分为两部分。
- Windows 每小时将系统时间更新为硬件时钟。(请注意,它在启动时也会执行此操作,从而加快测试速度)
- Parallels 错误地虚拟化了硬件时钟。我不得不说,我使用的是旧版本的 Parallels (PD9),我希望他们现在已经修复了这个问题。
我还没有找到关于这一点的明确说法,但我看到很多和我一样的人证实了同样的事情:Windows 每小时读取实时时钟 (RTC,或 BIOS 中的硬件时钟) 并与其重新同步。请参阅参考资料:#1,#2,#3,#4。
显然,其中大部分与 RTC 故障、BIOS 电池电量不足或与 *nix OS(在 RTC 中存储 UTC,而不是本地时间)双启动有关。但事实是 Windows 每小时都会这样做。我还没有找到禁用此功能的方法。
此外,Parallels 本身并不保留硬件时钟,而是在 VM 配置文件中保留偏移量(与系统时间的偏移量)。问题是,这个偏移量没有正确考虑 DST。例如,我在西班牙马德里有一台主机 Mac,通常为 UTC+1,但目前在夏令时为 UTC+2。当我在客户机中设置时间时,Parallels 会计算我的客户机时间和主机时区之间的差异,而不考虑 DST。
我们来举个例子:
当前时间为 UTC 00:00。
马德里标准时间为 UTC+1,即 01:00。但目前是 DST,UTC+2,02:00。我将客户机设置为 02:00,Windows 尝试将其写入 RTC,Parallels 计算客户机时间与马德里标准时间 (01:00) 之间的差异,并保存<TimeShift>-3600</TimeShift>
到配置文件中(文件仅在重新启动时更新,我猜想此变量在运行时会在内存中跟踪)。因此,每次 Windows 读取 RTC(Parallels 读取主机系统时间)时,它都会认为 RTC 设置为 HostTime-3600s(-1 小时)并更新时间。
我知道我的客户机设置很复杂(手动设置为开罗以找到没有夏令时的时区),我想我应该相信 Parallels,看看在客户机和主机都设置为正确时区(马德里,夏令时)的情况下它是否正常工作。不,它仍然搞砸了。
解决方案:
我找不到禁止 Windows 每小时读取 RTC 的方法,因此暂时我强制主机使用不使用 DST 的时区(例如开罗,UTC+2)。这有效。当我将客户机时间保存为 02:00,而开罗时间(UTC+2)为 02:00 时,Parallels 会保存<TimeShift>0</TimeShift>
。
丑陋的。
答案2
软件可以使用两种时间:系统时间和本地时间。无论 DST 设置如何,都不应更新系统时间。本地时间是经过调整的时间。如果不是这种情况,则您的操作系统或更可能是应用程序存在编程错误。可能是应用程序正在使用 GetLocalTime(),而它应该使用 GetSystemTime(),并且 UI 错误地指出它是系统时间。
答案3
几天来我一直在为这个系统事件而苦苦挣扎。多次尝试禁用或配置时间同步服务都失败了。我最终找到了一种解决方法,可以防止系统每 3600 秒调整一次系统时间。
Win32 API 提供了SetSystemTime
函数可将系统时间设置为任意所需时间。PowerShell 提供了更方便的函数Set-Date
包装 API 调用。通过调用,Set-Date -Adjust 0
系统时间保持不变,但系统看起来还不错。但缺点是,需要每 3600 秒重复一次调用,以防止系统事件持续发生。因此,我运行 PowerShell 脚本来注册每 59 分钟触发一次的计划任务:
$taskName = "Mock System Time Synchronization"
$taskDescription = "This task prevents the system to synchronize the system time with the hardware clock every 3600 seconds."
$startTime = (Get-Date) + (New-TimeSpan -Minutes 1)
$action = New-ScheduledTaskAction -Execute 'Powershell.exe' -Argument '-WindowStyle Hidden -command "& Set-Date -Adjust 0"'
$trigger = New-ScheduledTaskTrigger -Once -At $startTime -RepetitionInterval (New-TimeSpan -Minutes 59)
Register-ScheduledTask -Action $action -Trigger $trigger -TaskName $taskName -Description $taskDescription -User "System"