API Reference

WebNTP API 仕様

WebNTP は 3 つのプロトコルで時刻を配信します。いずれもレスポンスは共通の JSON フォーマット (Time over HTTPS を除く)で、クライアントは往復遅延からクロックオフセットを算出します。

概要

公開サーバーのベース URL は https://webntp.shogo82148.com です。自前でサーバーを立てる場合は webntp -serve :8080 で起動します。エンドポイントは次の 3 つです。

メソッド / パスプロトコル用途
GET /jsonHTTPJSON でサーバー時刻を返す
GET /websocketWebSocket接続を張ったまま時刻を送受信
HEAD /.well-known/timeHTTPSヘッダーで時刻を返す(軽量)

ブラウザから試す

このページを配信している公開サーバー webntp.shogo82148.com に対して、3 つのプロトコルを実際に呼び出せます。レスポンスと、そこから計算したオフセット・往復遅延が表示されます。

「実行」を押すと結果がここに表示されます。

レスポンスフィールド

/json/websocket が返す JSON は共通です。タイムスタンプはすべて Unix エポック秒(小数で秒未満を表現)です。

フィールド説明
idstringサーバーのホスト名。
itnumberInitiate Time — クライアントが送信したリクエスト時刻。送らなかった場合は 0。
stnumberSend Time — サーバーが応答を送信した時刻。
timenumberサーバー時刻。st と同じ値(後方互換のためのフィールド)。
leapnumberTAI − UTC の秒数(next より前の値)。
nextnumber次回、または直近の閏秒のタイムスタンプ。
stepnumber閏秒の方向。正の閏秒: 1、負の閏秒: −1。

JSON over HTTP

GET /json?<timestamp>

クエリ文字列にクライアントの送信時刻(エポック秒)を渡すと、サーバーはそれを it に反映して返します。クエリを省略すると it は 0 になります。

request
curl -s 'https://webntp.shogo82148.com/json?1788697606.057'
response · 200 application/json
{
  "id": "webntp.shogo82148.com",
  "it": 1788697606.057,
  "st": 1788697671.734335,
  "time": 1788697671.734335,
  "leap": 36,
  "next": 1483228800,
  "step": 1
}

WebSocket

WS /websocket

サブプロトコル webntp.shogo82148.com で接続します。テキストフレームでタイムスタンプ(エポック秒)を送ると、同じ JSON フォーマットで応答が返ります。接続を維持したまま複数回問い合わせでき、最も高精度な同期が可能です。無通信が続くとサーバー側から切断されます。

wscat
$ wscat --connect wss://webntp.shogo82148.com/websocket \
    --subprotocol webntp.shogo82148.com
> 1558915619.944235
< {"id":"webntp.shogo82148.com","it":1558915619.944235,"st":1558916776.363423, }
ブラウザからは new WebSocket(url, ["webntp.shogo82148.com"]) のようにサブプロトコルの指定が必須です。指定しないとハンドシェイクに失敗します。
JavaScript
const conn = new WebSocket(
  "wss://webntp.shogo82148.com/websocket",
  ["webntp.shogo82148.com"]
);
conn.onopen = () => conn.send((Date.now() / 1000).toString());
conn.onmessage = (ev) => {
  const r = JSON.parse(ev.data);
  const end = Date.now();
  const delay = end - r.it * 1000;
  const offset = r.st * 1000 - end + delay / 2;
  console.log("offset", offset, "ms");
};

Time over HTTPS

HEAD /.well-known/time

ボディを持たず、レスポンスヘッダー X-Httpstime にサーバー時刻(エポック秒)を入れて返します。FreeBSD の Time over HTTPS specification に基づく、最も軽量な方式です。

request / response
$ curl -I https://webntp.shogo82148.com/.well-known/time
HTTP/1.1 204 No Content
Access-Control-Allow-Origin: *
Access-Control-Expose-Headers: X-Httpstime
Cache-Control: no-cache
X-Httpstime: 1788697967.925993

同期アルゴリズム

クライアントは NTP と同じ考え方で、往復遅延を測ってクロックオフセットを推定します。送信時刻 it、応答受信時刻 end、サーバー送信時刻 st から次のように計算します。

計算式
# 往復遅延(ネットワークの往復にかかった時間)
delay  = end - it

# クロックオフセット(サーバー時刻 − クライアント時刻)
# 遅延の半分を片道分として補正する
offset = st - end + delay / 2

推定した offset をローカル時計に足すことで、サーバーに同期した時刻が得られます。往復遅延が小さいほど推定精度は高くなるため、WebSocket が最も有利です。

閏秒について

レスポンスの leap / next / step は閏秒に関するメタデータです。閏秒は将来的に廃止が予定されていますが、後方互換のため、直近に挿入された閏秒(2017-01-01 の正の閏秒、TAI − UTC = 36 秒)の情報を返し続けます。

本仕様は NICT(情報通信研究機構)の http/https を利用した時刻配信 のドキュメントを参考にしています。

CORS

すべてのエンドポイントは Access-Control-Allow-Origin: * を返すため、任意のオリジンのブラウザから利用できます。Time over HTTPS では Access-Control-Expose-Headers: X-Httpstime により、JavaScript からヘッダーを読み取れます。応答はいずれも Cache-Control: no-cache を返します。キャッシュへの保存は可能ですが、再利用の前にサーバーへの再検証が必要です。

← トップに戻る