Home > Windows にまつわる e.t.c.

PowerShell で PC 時刻を NTP に合わせる


Windows PC は、NTP から時刻を取得して、自動的に時刻補正をするのですが、PC の日時が大幅にズレていると時刻同期しない仕様になっています

このため、新しく購入した PC とか、マザーボードのバッテリーを交換した時等で、PC の日時が大幅に狂っている時は手動で時刻調整をする必要があります

この作業が思いのほか手間なので、NTP から時刻取得して、PC 時刻を調整するスクリプトを書きました
(NTP 実装で ChatGPT に頑張ってもらったのは内緒です w)

NTP 時刻を取ってくるところは、独立した関数にしていますので、他の用途にも流用できると思います

 

 

時刻調整方法

PowerShell を管理者権限で開いて、以下をコピペしてください

時刻調整スクリプトをダウンロードして実行します

$ModuleName = "GetNtpTime"
$GitHubName = "MuraAtVwnet"
$URI = "https://raw.githubusercontent.com/$GitHubName/$ModuleName/refs/heads/main/SetNtpTime.ps1"
$OutFile = "~/SetNtpTime.ps1"
Invoke-WebRequest -Uri $URI -OutFile $OutFile
& $OutFile

 

 

GitHub からのスクリプト ダウンロードが失敗する場合

PowerShell を管理者権限で開いて、一番下にあるスクリプトをコピペしてください

 

 

オプション

-PreferIPv4

IPv4 を優先する

-Server

NTP Server を指定する

省略時は以下 NTP を使用

ntp.nict.jp
time.google.com
pool.ntp.org

 

 

Windows PowerShell を使っている方へ

Windows PowerShell でスクリプトがエラーになって実行出来ない場合は以下コマンドを PowerShell のプロンプトにコピペしてください

if((Get-ExecutionPolicy) -ne 'RemoteSigned'){Set-ExecutionPolicy RemoteSigned -Scope CurrentUser -Force}

 

 

動作確認

Windows PowerShell 5.1
PowerShell 7.5.4 (Windows)

Linux/Mac でも動くとは思いますが、動作確認していません

 

 

GitHub

以下リポジトリで公開しています

https://github.com/MuraAtVwnet/GetNtpTime

git@github.com:MuraAtVwnet/GetNtpTime.git

 

時刻合わせスクリプト

#################################################################
#
# NTP から現在時刻を取得して PC の時刻を修正する
#
#################################################################

#################################################################
# NTP Time 取得
#################################################################
function GetNtpTime {
    param(
        [string[]]$Server = @("ntp.nict.jp", "time.google.com", "pool.ntp.org"),
        [int]$Port = 123,
        [int]$TimeoutMs = 3000,
        [int]$Retry = 2,
        [switch]$PreferIPv4 # IPv4 を優先する
    )

    function Convert-NtpTimestampToUtc([byte[]]$resp) {
        # Transmit Timestamp (bytes 40-47)
        $intPart  = [BitConverter]::ToUInt32($resp[43..40], 0)
        $fracPart = [BitConverter]::ToUInt32($resp[47..44], 0)

        $ms = ($intPart * 1000.0) + ($fracPart * 1000.0 / 0x100000000)

        $epoch = [DateTime]::SpecifyKind([DateTime]"1900-01-01T00:00:00", [DateTimeKind]::Utc)
        $epoch.AddMilliseconds($ms)
    }

    # NTP request packet (48 bytes)
    $req = New-Object byte[] 48
    $req[0] = 0x1B

    $errors = New-Object System.Collections.Generic.List[string]

    foreach ($s in $Server) {
        # Resolve all IPs
        try {
            $ips = [System.Net.Dns]::GetHostAddresses($s)
            if (-not $ips -or $ips.Count -eq 0) {
                throw "DNS解決結果が空です。"
            }

            if ($PreferIPv4) {
                $ips = @(
                    $ips | Where-Object { $_.AddressFamily -eq [System.Net.Sockets.AddressFamily]::InterNetwork }
                    $ips | Where-Object { $_.AddressFamily -eq [System.Net.Sockets.AddressFamily]::InterNetworkV6 }
                )
            }
        }
        catch {
            $errors.Add("[$s] DNS解決失敗: $($_.Exception.Message)")
            continue
        }

        foreach ($ip in $ips) {
            for ($try = 0; $try -le $Retry; $try++) {
                $udp = $null
                try {
                    # UdpClient
                    $udp = New-Object System.Net.Sockets.UdpClient($ip.AddressFamily)
                    $udp.Client.ReceiveTimeout = $TimeoutMs

                    # Windows PowerShell 5.1 でエラーになる対策
                    $ep = New-Object System.Net.IPEndPoint($ip, $Port)
                    $udp.Connect($ep)

                    [void]$udp.Send($req, $req.Length)

                    $remote = $null
                    $resp = $udp.Receive([ref]$remote)

                    if (-not $resp -or $resp.Length -lt 48) {
                        throw "NTP応答が不正(Length=$($resp.Length))"
                    }

                    $utc = Convert-NtpTimestampToUtc $resp
                    $local = $utc.ToLocalTime()

                    return [PSCustomObject]@{
                        Server    = $s
                        Address   = $ip.IPAddressToString
                        UtcTime   = $utc
                        LocalTime = $local
                        TimeoutMs = $TimeoutMs
                        Retry     = $Retry
                    }
                }
                catch {
                    $errors.Add("[$s / $($ip.IPAddressToString) / try=$try] $($_.Exception.Message)")
                }
                finally {
                    if ($udp) { $udp.Close(); $udp.Dispose() }
                }
            }
        }
    }

    $detail = ($errors | Select-Object -Last 12) -join "`n"
    throw "NTP時刻取得に失敗しました。UDP/123遮断 or IPv6/IPv4経路不調の可能性。`n直近のエラー:`n$detail"
}

#################################################################
# main
#################################################################

# PC の時刻を合わせる
Set-Date -Date (GetNtpTime).LocalTime
 

 

 

back.gif (1980 バイト)

home.gif (1907 バイト)

Copyright © MURA All rights reserved.