WindowsのNTP設定の勘所

# 本記事の概要

主にWindowsをNTPクライアントとして動作させる時の事を書いています。

詳細な説明は、

**Windows タイム サービスのしくみ | Microsoft Docs**
https://docs.microsoft.com/ja-jp/windows-server/networking/windows-time-service/how-the-windows-time-service-works

**Windows タイム サービスのツールと設定 | Microsoft Docs**
https://docs.microsoft.com/ja-jp/windows-server/networking/windows-time-service/windows-time-service-tools-and-settings

とか、
よくまとめられている、

**Windows 10の時刻同期設定 - がとらぼ**
https://gato.intaa.net/archives/12107

とか、

**Windows Server の NTP クライアントをちゃんと設定する**
https://ao-zai.blogspot.com/2016/08/windows-server-ntp.html

とか、その他解説読んでもらうとして、
分かりづらいところを感覚的な単語でピックアップしていきます。ちなみに、Windows 10です。

# Windowsの時刻同期の挙動

**w32time**サービス(GUIのサービス一覧では表示名「**Windows Time**」)により時刻同期されます。
通常、このサービスのスタートアップの種類は`手動（トリガー開始）`になっています。
このサービスが実行中であれば時刻同期を行うのですが、最近のWindowsでは、消費電力とパフォーマンスの関係から、単純な動作にはなっていないようです。
**トリガー開始**というのは、タスク スケジューラの**Microsoft->Windows->Time Synchronization**のタスクに設定されているトリガーの事です。（２つありますが、どっちかまでは調べていません。）
さらにこのトリガーが特殊なため、タスク スケジューラからはトリガーの変更ができません。
また、**トリガーが有効になっていると、w32timeサービスを開始しても、即座に停止してしまいます。**
なので、まずは

```コマンドプロンプト（管理者権限）.
sc triggerinfo w32time delete
```

を行って、この特殊なトリガーを削除してやると、即座に停止しなくなります。
その上で、**スタートアップの種類**を`自動`または`自動（遅延開始）`にし適用してから、`開始`する必要があります。
あと、設定を変更する場合、サービスを停止すべき（多分書き込みが無かったことにされます）。

# レジストリでの設定

だいたいの話は他のサイトを読んでもらうとして、ややこしいところをピックアップ。

### 「NtpServer」の値

`[NTPサーバのURLまたはIPアドレス],[パラメータ(16進表記を行う)]`
といった感じにします。
`ntp.nict.jp,0x8`とか`192.168.0.10,0x8`とかですね。
カンマ以降は、ビット演算的になっています。
**0x8**を指定すると、PCがクライントとして時刻同期を見に行くモード、それ以外は自分もNTPサーバの場合みたいな感じで、普通の用途では使わなさそう。
**0x1のビットを指定しない場合**、MinPollInterval ～ MaxPollInterval の間で、同期が実行されます。（時計のずれが統計的に(？)大きいとMin寄り、ずれが小さいとMax寄りの間隔になる？ 最初はMin間隔で実行して、徐々にMaxに寄って行くイメージ。）
**0x1のビットを指定する場合**、SpecialPollInterval のみの間隔で、同期が実行されます。
Windows的には**0x1ビット指定あり**がデフォルトらしいですが、世の中的には**0x1ビット指定無し**がデフォルトらしいです。（また、Windowsのトリガ条件は、日曜の午前1時に起動していた、らしいですが、その時刻に起動していない場合、なかなか時刻合わせしない・・・？）
（**0x2, 0x4**は、NTPサーバ向けらしいので、クライアントには関係なし。）
具体的には、下記のように覚えればよいでしょう。
・`0x8`：可変間隔で時刻同期する場合 (Linux等の標準)
・`0x9`：一定間隔で時刻同期する場合 (Windowsの標準)
※ 基本的にレジストリで設定する値の**単位は秒**ですが、 **このMix,Maxの単位は2のべき乗(秒)** なので注意。**Specialは秒**ですが、カーソル位置のデフォルトが16進数なので、意図しない時間になっていることがあるので注意。
※ コンパネの時刻同期実行ダイアログ（インターネット時刻タブの設定の変更から開くダイアログ）は、**開くだけで NtpServer の値を書き換えようとする**ので、この辺の設定しているときには開かないこと。

### slewモードについて

時間が巻き戻るとおかしくなるシステムで本領を発揮する同期方法ですが、「1秒間につき最大で0.0005秒」（＝**1秒合わせ終わるのに約33.33分**）かかるので、数秒離れた状態で実行するとすごく長い時間がかかります。
また、**一度slewモードに入ると想定した秒数ずらし終わるまでずらし続ける（？）** ようです。
例えば、5秒進めるつもりでslewモードに入ってから、0.5秒進めたあと、stepモードでぴったり合わせた場合、そのあともずれていって、**4.5秒進みすぎた状態まで行ってからslewモード終了**する的な。
slewモードで同期する条件は２つあって、両方満たしてかつ短い方が適用されます。
**条件式１**は、**NTPサーバとPCの時刻差が MaxAllowedPhaseOffset (秒)より近いとSlewモード**、離れているとStepモードで時刻合わせが実行されます。
**条件式２**は、計算結果が、デフォルトでシングルプロセッサなら約30分、マルチプロセッサなら約45分になるので、**普通の用途では読み飛ばしてOK** (ドメイン(Active Directory？)環境下では違う値になるので注意)
なので、実質的に条件式１だけ見ればよく、**MaxAllowedPhaseOffset = slewモードのしきい値**とみればOK。時間が巻き戻ってもいいシステムでは、`1`(秒)くらいにしておくのが良いと思います。

# 即座に同期したいとき
管理者権限付きコマンドプロンプトで
`w32tm /resync`
**「w32time」ではない**ので注意。
~~たぶん、強制Stepモードです。コンパネのダイアログでの手動同期も、おそらくこのコマンドが走っています。（Slewになる範囲内なのにStepモードで合ったため。でも、1.2秒ずれのときにslewモードに入ったこともあって、詳細は不明）~~ (検証不十分なので打ち消し線にしておきます)

# 動作確認方法
**イベント ビューアー**の**Windows ログ->システム**にあるソースが**Kernel-GeneralかTime-Service**のもの。
・ソース：Time-Service、イベント`ID：37`、「タイム プロバイダー NtpClient は現在 ntp.nict.jp,0x8 (～) から有効な時間データを受信しています。」みたいなのは、接続完了。
・ソース：Time-Service、イベント`ID：35`、「タイム サービスはシステム時刻とタイム ソース ntp.nict.jp,0x8 (～) (参照 ID ～) の同期をとっています。現在のローカル階層番号は 2 です。 」みたいなのは、**Slewモード実行開始**のようです。完了は何も出ないもよう。
・ソース：Time-Service、イベント`ID：158`のエラーは、通常、無視してOKらしい。
・ソース：Kernel-General、イベント`ID：1`、「システム時刻は ‎2019‎-‎12‎-‎05T07:08:55.882950200Z から ‎2019‎-‎12‎-‎05T07:08:54.000000000Z に変更されました。変更の理由: An application or system component changed the time。プロセス: '\Device\HarddiskVolume3\Windows\System32\dllhost.exe' (PID ～)。」は、手動同期またはStepモード実行時。何故か2回でて、2回目はほとんど時刻を変えない。TからZの間が時刻（秒の小数点以下の単位も秒）だが、世界標準時（日本の9時間前）なので注意。

# 詳細なログの確認方法
Windows Time サービスのログについてより詳細なログを見る方法は、大まかには3段階あるようです。
簡易：w32tm /query /status
詳細：w32tm /query /status /verbose
ファイル出力詳細：w32tm /debug /enable /file:C:\Windows\Temp\w32time.log
/size:1000000 /entries:0-300
また、現在設定の確認方法や別の視点からのデバッグ方法もこちらにまとめられています。

**簡易・詳細ログの出力方法等**
https://www.atmarkit.co.jp/ait/articles/1207/12/news146_2.html
**ファイル出力詳細ログの出力方法等**
https://www.atmarkit.co.jp/ait/articles/1207/12/news146_3.html
**ファイル出力詳細ログの解析方法等**
https://www.atmarkit.co.jp/ait/articles/1301/24/news105_2.html
**「イベント ビューアー」のログの詳細確認方法等**
https://www.atmarkit.co.jp/ait/articles/1301/24/news105.html

# バグ

少なくとも、NtpClientのカンマ以降に0x9が指定されている場合に、Spike状態になると時刻合わせをしなくなる設定の組み合わせがある、というバグがあるようです（年単位で放置されている模様）。

**タイム サービスが時刻を修正しない - Windows Server | Microsoft Docs**
https://docs.microsoft.com/ja-jp/troubleshoot/windows-server/identity/specialpollinterval-polling-interval-time-service-not-correct

バグについての解説は以下。
（ちなみにこの解説、NTPの状態遷移についても分かりやすい。）

**Spike 状態から抜けられない!?!? | Microsoft Docs**
https://docs.microsoft.com/en-us/archive/blogs/jpntsblog/spike

~~ただ、調べてみると、0x8だったとしても、同じようなバグがあるとどこかで見ました。~~
さらに試した・調べた結果、さらなる落とし穴がありました。

自前でGPS/GNSSで作ったNTPサーバなど、NTPサーバ側が突然大きめに時刻変動すると、`MaxNegPhaseCorrection`と`MaxPosPhaseCorrection`を`0xffffffff`にしていても、時刻合わせをしなくなるという現象があり、`w32tm /resync`をかけても、

- `w32tm /resync`→`「必要な時刻の変更が大きすぎたため～」`の表示が出る
- イベントログに矛盾した内容、`「システム時刻を 0 秒変更しなくてはならないことが判明しました。タイム サービスはシステム時刻を 4294967295 秒以上変更することはできません。～」`といったログが残る
- 詳細ログには`「*TOO BIG* Unset->Hold」`といった表示が出る

という不可解な現象が起きて、ネット上でも数人ハマった人が解決していないようでした。
結論からいうとこれです。

**Windows 10 (TH2) をインターネットに接続せずに使用すると、突然時刻がずれる！？ | Microsoft Docs**
https://docs.microsoft.com/en-us/archive/blogs/jpntsblog/windows-10-th2-%E3%82%92%E3%82%A4%E3%83%B3%E3%82%BF%E3%83%BC%E3%83%8D%E3%83%83%E3%83%88%E3%81%AB%E6%8E%A5%E7%B6%9A%E3%81%9B%E3%81%9A%E3%81%AB%E4%BD%BF%E7%94%A8%E3%81%99%E3%82%8B%E3%81%A8%E3%80%81

`インターネット上の SSL サーバーと接続し、時刻を取得する Secure Time 機能`というのが Windows 10 に追加されており、これがデフォルトで有効なのですが、インターネット環境が無い状態で有効になっている場合、Windows Time サービス がマニュアル通りの動きをしなくなります。そして、動作の仕組みは非公開というから、尚更たちが悪い。
（推測ですが、通常、SSL通信に使われる時刻情報は、証明局で有効期限が認証され、それなりに信頼性が高いという理論だと思いますが・・・完全なオフライン環境であれば、SSL通信時に使われる時計も狂っている可能性があります。それなのにSSLの時刻を強く信用されるため、時計が合わなくなる、のだと思います。）
これを`無効`にする（ 0 を設定する）と、通常のNTPの仕様通りの動きに戻ります。
変更の反映は、Windows Time サービスを再起動しても良いし、`w32tm /config /update`でも良いです。

```regedit.
レジストリキー : HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\W32Time\Config
値の名前 : UtilizeSslTimeData
有効:1 (既定値)
無効:0
```

さらに、レアケースだと思いますが（私が遭遇したケースｗ）、 Windows 10 IoT LTSB 等の書き込みプロテクト （ライトプロテクト、UWFフィルター、UWF(Unified Write Filter)、uwfmgr）が使える機器においておきる現象について。
書き込みフィルターが無効な状態で、Windowsを再起動またはシャットダウンするときに、Windowsが管理する領域とハードウェアクロック（RTC）の両方に書き込みが行われるようです。
次回起動時には、この領域のうちOSが書き込んだ情報が参照されて起動するようです。
どうなるかというと、上記の`UtilizeSslTimeData`が`1`の状態がしばらくあって、おかしな時刻（例えば2121年）になっていることに気付かず、書き込みフィルター`現在：オフ` `次回：オン`の状態にして、`UtilizeSslTimeData`を`0`にして、（NTP時刻合わせ出来ないほどズレた状態で）再起動すると、二度と自動時刻合わなくなります。時計合わせて再起動しても、またおかしな時刻に巻き戻るので、再起動の度におかしくなる。
解決するには、一度書き込みフィルターを`現在：オフ` `次回：オン`の状態にし、現在に近い時刻（出来れば現在時刻 or わずかに過去）に`date`と`time`コマンドで合わせなおし、再起動で直ります。
（この時、Windowsのダイアログから変更すると、レジストリのW32Timeの値が書き変わる可能性がありますので、コマンドから行うこと。）

## さらにバグらしき挙動

これまでのを全て対処した上でも、NTPサーバ側とクライアント側をほぼ同時に起動して、NTPサーバ側の時刻が安定しない場合、Windows側が同期する度に時刻が指数的にずれていく現象が発生。もう分からない。
現象が起きたのは、Windows起動時すぐの数回同期に合わせに行っている途中で、NTPサーバ側の時計が数年前→現在にジャンプした時に発生。Windows側は、現在→数年前→現在まではちゃんと追従し、その15秒後くらいから1ヶ月先にセットされた。以後同期ごとに指数的かつ揺らぎながらズレていく。2036年を超えたあたりで、以下のオーバーフローによって全く同期しなくなる。（酷い時には、2189年とかまで行ってる。）
Windowsから見ると、NTPサーバの時刻は安定していている前提なのかもしれない（年単位で飛ぶ事が想定されていないのかもしれない）。
NTPサーバ側は1回は時刻同期が走ってからNTPサーバサービス/デーモンを立ち上げるとか。いっそオフライン環境ではNTP使わずに、自前で時刻同期の仕組みを作るとか。

# NTPのオーバーフロー (2036年問題)
そして最終的に、個人レベルではどうしようも出来ない問題があります。
現行の世界中のNTPは、2036年の2月7日の途中でオーバーフローします。
Windows 10では、2021年時点では、2036/2/7になると、`MaxNegPhaseCorrection`の値が`0xffffffff`であろうとも、有効な時刻を受信したりしなかったりでした。2/8以降は全くダメでした。

詳細は下記等をご確認ください。

**2036年問題の解説とその解決策 - ari23の研究ノート**
https://ari23.hatenablog.com/entry/year-2036-problem-ntp

# NTPの仕組み概要

そもそもですが、NTPの時刻合わせの仕組みをざっくりと。
「NTP 仕組み」あたりでググると、以下などなど散々出てきますが、概要だけ。

**TCP/IP - NTPとは**
https://www.infraexpert.com/study/tcpip25.html

### サーバとクライアント群の構造

NTPは階層構造になっており、 stratum という番号で管理されています。
（原子時計やGNSS(GPS)等自身が startum 0 (NTPサーバではない)）
正確なタイムソースに合わせる最初のNTPサーバが stratum 1
以降、stratum 1 のNTPサーバにNTPクライアントとしてアクセスして同期しているNTPサーバが stratum 2 以降（最大15）
（上記URLの「◆　NTP - 階層構造」参照）

### 時刻同期の仕組み

クライアントからサーバへ、2秒置きくらいに複数回、時刻を問い合わせ、遅延を推測(※)等行い、正しいであろう時刻をセットする。
※：上記URLの「◆　NTP - 仕組み」参照。お互いの自分自身の時刻を交換することで、通信遅延時間が推定できる。

実際の同期時の挙動を詳しく知りたい方は、以下等で確認してください。

**第2回　w32timeデバッグ・ログとw32tmコマンド (1/3)：Windowsネットワーク時刻同期の基礎とノウハウ - ＠IT**
https://www.atmarkit.co.jp/ait/articles/0511/10/news124.html

なお、WindowsのNTPは、正確には SNTP（Simple Network Time Protocol）らしい。
その割に、複数個所に同期する設定できたり、サーバになれたりするみたい。
詳細は以下。

**Windows タイム サービスのしくみ - Microsoft Docs**
https://docs.microsoft.com/ja-jp/windows-server/networking/windows-time-service/how-the-windows-time-service-works

> 簡易ネットワーク タイム プロトコル
> 
> 2 つの主な違いは、SNTP に、エラーの管理と NTP を提供する複雑なフィルター処理システムがないことです。

# 設定を初期設定に戻す方法

**サービス停止した状態**で、以下のコマンドを管理者権限のあるコマンドプロンプトで実行

```コマンドプロンプト（管理者権限）.
w32tm /unregister
w32tm /register
```

※ **w32time ではないので注意**(w32time と打つのは、scと組み合わせるとき）
これで、サービスの登録・設定・トリガー、レジストリ設定も元に戻る。
（/unregister した時に、サービスやレジストリから一旦消える）