windows-time-sync
Diagnose and repair Windows clock drift / NTP sync failures — w32time service stopped, silent resync rejection due to MaxPosPhaseCorrection cap, and verifying via direct NTP socket query (HTTP time AP
SKILL.mdskills/devops/windows-time-sync/SKILL.md
GitHubWindows Time Sync / NTP Repair
Windows system clock maintenance — repairing w32time failures and NTP drift. Class-level skill covering: diagnosing stopped w32time, fixing the silent MaxPosPhaseCorrection rejection trap, configuring manual NTP peers, and verifying via authoritative NTP queries (not HTTP time APIs).
Symptom
dateoutput doesn't match real time; cron job timestamps or Discord logs look shifted.w32tm /query /statusshowsSource: Local CMOS ClockandStratum: 0 (unspecified).Get-Service w32timeshowsStopped.
Diagnosis Order
date '+%Y-%m-%d %H:%M:%S %Z (%z)'— local time + offset.w32tm /query /status— check Source / Stratum / Last Successful Sync Time.Source: Local CMOS Clock+Stratum: 0= never synced / service dead.
Get-Service w32time | Select-Object Status— confirm service state.
Fix Sequence
1. Start the service (set to Automatic)
powershell -NoProfile -Command "Start-Process powershell -Verb RunAs -ArgumentList '-NoProfile','-Command','Set-Service w32time -StartupType Automatic; Start-Service w32time; w32tm /resync /nowait' -Wait"
2. Configure manual NTP peers
The default time.windows.com,0x9 source can fail with "no time data was available".
Point directly at public pools:
powershell -NoProfile -Command "w32tm /config /manualpeerlist:'pool.ntp.org,0x8 time.nist.gov,0x8' /syncfromflags:manual /reliable:NO /update"
w32tm /resync /force
3. The Trap — large offsets are silently rejected (15-min cap)
If the clock drifted more than 15 minutes, w32tm /resync /force reports
"The command completed successfully" but the clock does not move. Windows'
default MaxPosPhaseCorrection / MaxNegPhaseCorrection is 900 seconds; offsets
beyond it are silently discarded with no error. Raise the cap, then it snaps:
powershell -NoProfile -Command "reg add 'HKLM\SYSTEM\CurrentControlSet\Services\W32Time\Config' /v MaxPosPhaseCorrection /t REG_DWORD /d 0xFFFFFFFF /f; reg add 'HKLM\SYSTEM\CurrentControlSet\Services\W32Time\Config' /v MaxNegPhaseCorrection /t REG_DWORD /d 0xFFFFFFFF /f; w32tm /config /update"
w32tm /resync /force
After this, w32tm /query /status /verbose should show Phase Offset: ~0.00s and
Stratum: 2 or 3 with a real NTP source.
Verification — Direct NTP Socket Query Is Ground Truth
HTTP time APIs are NOT reliable for verification. On 2026-07-31, timeapi.io and
worldtimeapi.org both returned times 16 minutes stale while the machine clock
matched pool.ntp.org and time.nist.gov within 0.1s via direct NTP socket queries.
The HTTP APIs were the wrong ones — the clock was correct.
Always verify with a raw NTP request (UDP 123) to multiple servers:
import socket, struct, time, datetime
def ntp_time(host='pool.ntp.org'):
req = b'\x1b' + 47*b'\x00'
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s.settimeout(5)
s.sendto(req, (host, 123))
data, _ = s.recvfrom(512)
t = struct.unpack('!12I', data)[10]
frac = struct.unpack('!12I', data)[11] & 0xFFFFFFFF
return t + frac/2**32 - 2208988800 # NTP epoch -> unix epoch
for host in ['pool.ntp.org', 'time.nist.gov']:
nt = ntp_time(host)
print(host, datetime.datetime.utcfromtimestamp(nt).strftime('%Y-%m-%d %H:%M:%S UTC'),
'| local:', datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S %Z'),
'| diff sec:', round(nt - time.time(), 1))
Diff within ~1s on both servers = clock is correct; the HTTP API was stale.
Pitfalls
w32tm /resync /rediscover /forcefails withThe parameter is incorrect—/rediscoverdoes not accept/force. Use plainw32tm /resync /force.- "The computer did not resync because no time data was available" = NTP peers not configured yet — run step 2 before resyncing.
- The w32time service being stopped is the root cause of long-term drift — fixing the clock
without setting the service to
Automaticmeans it drifts again on next reboot. - Cron jobs on Hermes use local wall-clock time, so a silently fast clock means "daily at 11:30 PM" fires 16 minutes early relative to real time. Clock drift → wrong reminder delivery times → user thinks reminders aren't logging properly.
w32tm /query /statusshowingLast Successful Sync Timethat looks recent does NOT mean the clock actually moved — the "Last Successful Sync Time" just records when the service tried. Check the Phase Offset and compare directly to a ground-truth NTP query.
Affected Skills
cron-watchdog— clock drift directly impacts missed-run detection and fire scheduling.fitness-accountability— daily 23:30 ET cron relies on local wall-clock time.