【Node.js】NTPで時刻オフセットを計算してみた
こんにちは、フリーランスエンジニアの太田雅昭です。
時刻関連
昨今のコンピュータはどれもこれも自動で時刻補正がなされています。それはNTPサーバーを介して行われるのですが、ネットワーク遅延などを考慮する必要があります。そのための計算があります。
ただその計算が、どうもややこしい。
計算してみた
ほとんどChatGPT大先生によるコードですが、以下のようになりました。
import { Client as NTP } from 'ntp-time';
async function getOffset(server: string) {
const ntp = new NTP(server, 123, { timeout: 1000 });
const clientSendTime = Date.now(); // クライアントがリクエストを送信した時刻(ミリ秒単位)
const ntpPacket = await ntp.syncTime();
const clientReceiveTime = Date.now(); // クライアントが応答を受信した時刻(ミリ秒単位)
// NTPパケットからタイムスタンプを抽出(秒単位)
const serverReceiveTime = (ntpPacket as any).rxTimestamp;
const serverTransmitTime = (ntpPacket as any).txTimestamp;
// NTPのタイムスタンプは1900年からの秒数なので、1970年基準のミリ秒に変換
const NTP_TIMESTAMP_BASE = 2208988800000; // 1900年から1970年までのミリ秒
const serverReceiveTimeMs = serverReceiveTime * 1000 - NTP_TIMESTAMP_BASE;
const serverTransmitTimeMs = serverTransmitTime * 1000 - NTP_TIMESTAMP_BASE;
// ネットワーク遅延の推定値を計算
const roundTripDelay =
clientReceiveTime -
clientSendTime -
(serverTransmitTimeMs - serverReceiveTimeMs);
// オフセットを計算(クライアントとサーバーの時刻差)
const localClockOffset =
(serverReceiveTimeMs -
clientSendTime +
(serverTransmitTimeMs - clientReceiveTime)) /
2;
console.log({
server,
clientSendTime,
clientReceiveTime,
serverReceiveTimeMs,
serverTransmitTimeMs,
roundTripDelay,
localClockOffset,
});
return localClockOffset;
}
const SERVERS = [
'a.st1.ntp.br',
'ntp.jst.mfeed.ad.jp',
'ntp.nict.jp',
'time.google.com',
'time.aws.com',
'time.cloudflare.com',
];
for (const server of SERVERS){
const offset = getOffset(server);
...
}
これで時刻のずれを計算できるのですが、何がややこしいって。ntp-timeを使ったのですが、このライブラリの型定義と実際に吐き出されるオブジェクトとフィールドが違うし、タイムスタンプの起点もフィールドによって違うしで、もう何が何だかわからずです。今でも良くわかっていません。とにかくChatGPT大先生には感謝です。
追記:2024/02/06
計算まで行ってくれるライブラリがありました。。。
複数サーバーから計算を行ってくれるようです。これを使いたいと思います。なおデフォルトで設定されているサーバーは、他NTPサーバーからランダムに情報を取ってくるもののようです。本番では選定したサーバーを使用した方が良さそうです。
小話
今日は朝から久しぶりにココアを飲みました。コンビニのペットボトルです。ただ、やっぱり自分で入れた方が美味しいですね。。。なんなんでしょうこれは。やはり牛乳の新鮮さなどの関係でしょうか。喫茶店をやっていた頃は、無調整の牛乳と、あとは何か美味しい砂糖を特別に使ったりしていました。ペルーシェとかいう名前だったと思います。ココアととても良くあって、美味しかったです。