コンテンツにスキップ

ネットワーク時間⚓︎

初級

Symbol ブロックチェーン における時間は「ネットワーク時間」で測定されます。これは、チェーンの最初の ブロック から経過したミリ秒数として定義されます。

この簡単なチュートリアルでは、API 呼び出しによって返されるネットワーク時間を UTC のような標準的なタイムスタンプに変換する方法を説明します。

前提条件⚓︎

このチュートリアルでは、SDK を必要とせずに Symbol REST API を使用します。 HTTP リクエストを行う方法さえあれば実行可能です。

完全なコード⚓︎

このチュートリアルの完全なコード一覧を以下に示します。 詳細な手順ごとの説明は次のセクションで行います。

import datetime
import json
import os
import urllib.request

NODE_URL = os.getenv('NODE_URL', 'https://whydah.symbolmain.net:3001')
print(f'Using node {NODE_URL}')

try:
    # Fetch Nemesis timestamp
    properties_path = '/network/properties'
    print(f'Fetching network properties from {properties_path}')
    with urllib.request.urlopen(
        f'{NODE_URL}{properties_path}') as response:
        response_json = json.loads(response.read().decode())
        nemesis_datetime = datetime.datetime.fromtimestamp(
            int(response_json['network']['epochAdjustment'].rstrip('s')),
            tz=datetime.timezone.utc)

    # Fetch current network timestamp
    time_path = '/node/time'
    print(f'Fetching current network time from {time_path}')
    with urllib.request.urlopen(f'{NODE_URL}{time_path}') as response:
        response_json = json.loads(response.read().decode())
        network_ms = int(
            response_json['communicationTimestamps']['receiveTimestamp'])

    network_datetime = nemesis_datetime + datetime.timedelta(
        milliseconds=network_ms)

    print(f'\nNemesis time (UTC): {nemesis_datetime}')
    print(f'Network time (ms since Nemesis): {network_ms}')
    print(f'Network time (UTC): {network_datetime}')
except Exception as error:
    print(error)

Download source

const NODE_URL = process.env.NODE_URL ||
    'https://whydah.symbolmain.net:3001';
console.log(`Using node ${NODE_URL}`);

try {
    // Fetch Nemesis timestamp
    const propertiesPath = '/network/properties';
    console.log(`Fetching network properties from ${propertiesPath}`);
    const propertiesResponse =
        await fetch(`${NODE_URL}${propertiesPath}`);
    const propertiesJson = await propertiesResponse.json();
    const nemesisStr = propertiesJson.network.epochAdjustment;
    const nemesisSeconds = parseInt(nemesisStr.replace('s', ''), 10);
    const nemesisDatetime = new Date(nemesisSeconds * 1000);

    // Fetch current network timestamp
    const timePath = '/node/time';
    console.log(`Fetching current network time from ${timePath}`);
    const timeResponse = await fetch(`${NODE_URL}${timePath}`);
    const timeJson = await timeResponse.json();
    const networkMs = BigInt(
        timeJson.communicationTimestamps.receiveTimestamp);

    const networkDatetime = new Date(
        nemesisDatetime.getTime() + Number(networkMs));

    console.log(
        `\nNemesis time (UTC): ${nemesisDatetime.toISOString()}`);
    console.log(`Network time (ms since Nemesis): ${networkMs}`);
    console.log(`Network time (UTC): ${networkDatetime.toISOString()}`);
} catch (error) {
    console.error(`Error: ${error.message}`);
}

Download source

//JAVA 21+
//DEPS org.symbol:symbol-sdk:3.3.1

import java.io.IOException;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.net.http.HttpResponse.BodyHandlers;
import java.time.Instant;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;

public final class NetworkTime {
    private static final ObjectMapper JSON_MAPPER = new ObjectMapper();

    private static final HttpClient HTTP_CLIENT =
        HttpClient.newHttpClient();

    private final String nodeUrl = System.getenv().getOrDefault(
        "NODE_URL", "https://whydah.symbolmain.net:3001");

    public static void main(final String[] args) {
        try {
            new NetworkTime().run();
        } catch (final Exception ex) {
            System.out.println(null == ex.getMessage()
                ? ex.toString()
                : ex.getMessage());
        }
    }

    private void run() throws IOException, InterruptedException {
        System.out.printf("Using node %s%n", nodeUrl);

        // Fetch Nemesis timestamp
        final String propertiesPath = "/network/properties";
        System.out.printf(
            "Fetching network properties from %s%n", propertiesPath);
        final HttpRequest propertiesRequest = HttpRequest.newBuilder(
            URI.create(nodeUrl + propertiesPath)).GET().build();
        final HttpResponse<String> propertiesResponse =
            HTTP_CLIENT.send(
                propertiesRequest, BodyHandlers.ofString());
        final JsonNode propertiesJson = JSON_MAPPER.readTree(
            propertiesResponse.body());
        final String nemesisStr = propertiesJson.get("network")
            .get("epochAdjustment").asText();
        final long nemesisSeconds =
            Long.parseLong(nemesisStr.replace("s", ""));
        final Instant nemesisDatetime =
            Instant.ofEpochSecond(nemesisSeconds);

        // Fetch current network timestamp
        final String timePath = "/node/time";
        System.out.printf(
            "Fetching current network time from %s%n", timePath);
        final HttpRequest timeRequest = HttpRequest.newBuilder(
            URI.create(nodeUrl + timePath)).GET().build();
        final HttpResponse<String> timeResponse = HTTP_CLIENT.send(
            timeRequest, BodyHandlers.ofString());
        final JsonNode timeJson = JSON_MAPPER.readTree(
            timeResponse.body());
        final long networkMs = timeJson
            .get("communicationTimestamps")
            .get("receiveTimestamp").asLong();

        final Instant networkDatetime =
            nemesisDatetime.plusMillis(networkMs);

        System.out.printf("%nNemesis time (UTC): %s%n",
            nemesisDatetime);
        System.out.printf("Network time (ms since Nemesis): %d%n",
            networkMs);
        System.out.printf("Network time (UTC): %s%n",
            networkDatetime);
    }
}

Download source

コード解説⚓︎

コードはネメシスブロックのタイムスタンプと現在のネットワーク時間を取得します。 その後、それらを足し合わせて標準的な UTC での現在時刻を求めます。

ネメシスタイムスタンプの取得⚓︎

    properties_path = '/network/properties'
    print(f'Fetching network properties from {properties_path}')
    with urllib.request.urlopen(
        f'{NODE_URL}{properties_path}') as response:
        response_json = json.loads(response.read().decode())
        nemesis_datetime = datetime.datetime.fromtimestamp(
            int(response_json['network']['epochAdjustment'].rstrip('s')),
            tz=datetime.timezone.utc)
    const propertiesPath = '/network/properties';
    console.log(`Fetching network properties from ${propertiesPath}`);
    const propertiesResponse =
        await fetch(`${NODE_URL}${propertiesPath}`);
    const propertiesJson = await propertiesResponse.json();
    const nemesisStr = propertiesJson.network.epochAdjustment;
    const nemesisSeconds = parseInt(nemesisStr.replace('s', ''), 10);
    const nemesisDatetime = new Date(nemesisSeconds * 1000);
        final String propertiesPath = "/network/properties";
        System.out.printf(
            "Fetching network properties from %s%n", propertiesPath);
        final HttpRequest propertiesRequest = HttpRequest.newBuilder(
            URI.create(nodeUrl + propertiesPath)).GET().build();
        final HttpResponse<String> propertiesResponse =
            HTTP_CLIENT.send(
                propertiesRequest, BodyHandlers.ofString());
        final JsonNode propertiesJson = JSON_MAPPER.readTree(
            propertiesResponse.body());
        final String nemesisStr = propertiesJson.get("network")
            .get("epochAdjustment").asText();
        final long nemesisSeconds =
            Long.parseLong(nemesisStr.replace("s", ""));
        final Instant nemesisDatetime =
            Instant.ofEpochSecond(nemesisSeconds);

ネメシスブロックの作成時間は固定のネットワークプロパティであり、 /network/properties GET エンドポイントを使用して取得できます。 返される値( epochAdjustment )は、 s サフィックスを削除して整数に変換すると、 UNIX タイムスタンプ になります。 つまり、UNIX エポック(1970年1月1日 00:00:00 UTC)から経過したうるう秒を除いた秒数です。

このチュートリアルでは説明の目的でネットワークから取得していますが、これは固定値であるため、必要に応じて定数として扱い、ハードコードすることも可能です。 Symbolの メインネット の場合、値は 1615853185 であり、これは 2021-03-16T00:06:25Z (2021年3月16日 午前0時06分25秒 UTC)に相当します。

現在のネットワーク時間の取得⚓︎

    time_path = '/node/time'
    print(f'Fetching current network time from {time_path}')
    with urllib.request.urlopen(f'{NODE_URL}{time_path}') as response:
        response_json = json.loads(response.read().decode())
        network_ms = int(
            response_json['communicationTimestamps']['receiveTimestamp'])
    const timePath = '/node/time';
    console.log(`Fetching current network time from ${timePath}`);
    const timeResponse = await fetch(`${NODE_URL}${timePath}`);
    const timeJson = await timeResponse.json();
    const networkMs = BigInt(
        timeJson.communicationTimestamps.receiveTimestamp);
        final String timePath = "/node/time";
        System.out.printf(
            "Fetching current network time from %s%n", timePath);
        final HttpRequest timeRequest = HttpRequest.newBuilder(
            URI.create(nodeUrl + timePath)).GET().build();
        final HttpResponse<String> timeResponse = HTTP_CLIENT.send(
            timeRequest, BodyHandlers.ofString());
        final JsonNode timeJson = JSON_MAPPER.readTree(
            timeResponse.body());
        final long networkMs = timeJson
            .get("communicationTimestamps")
            .get("receiveTimestamp").asLong();

照会先の ノード が認識している現在のネットワーク時間は、 /node/time GET エンドポイントを使用して取得されます。 ネットワーク内のノードは通常同期されているため、ほぼ同じ時間を返します。

実際に照会されるプロパティは communicationTimestamps.receiveTimestamp であり、これはリクエストがノードによって受信された時間を表します。

この値は ネットワーク時間 、つまりネメシスブロックが作成されてから経過したミリ秒数で表されます。

変換⚓︎

現在のネットワーク時間から UTC への変換は、単位(秒またはミリ秒)を揃えることに注意しながら、2つの数値を足し合わせるだけで済みます。

    network_datetime = nemesis_datetime + datetime.timedelta(
        milliseconds=network_ms)

    print(f'\nNemesis time (UTC): {nemesis_datetime}')
    print(f'Network time (ms since Nemesis): {network_ms}')
    print(f'Network time (UTC): {network_datetime}')
    const networkDatetime = new Date(
        nemesisDatetime.getTime() + Number(networkMs));

    console.log(
        `\nNemesis time (UTC): ${nemesisDatetime.toISOString()}`);
    console.log(`Network time (ms since Nemesis): ${networkMs}`);
    console.log(`Network time (UTC): ${networkDatetime.toISOString()}`);
        final Instant networkDatetime =
            nemesisDatetime.plusMillis(networkMs);

        System.out.printf("%nNemesis time (UTC): %s%n",
            nemesisDatetime);
        System.out.printf("Network time (ms since Nemesis): %d%n",
            networkMs);
        System.out.printf("Network time (UTC): %s%n",
            networkDatetime);

このチュートリアルでは、プロセスを示すために手動で加算を行っています。 Symbol SDK を使用している場合は、 がより便利な抽象化を提供します。

出力⚓︎

以下に示す出力は、プログラムの典型的な実行結果に対応しています。

1
2
3
4
5
6
7
Using node https://whydah.symbolmain.net:3001
Fetching network properties from /network/properties
Fetching current network time from /node/time

Nemesis time (UTC): 2021-03-16T00:06:25.000Z
Network time (ms since Nemesis): 159102619442
Network time (UTC): 2026-03-31T11:16:44.442Z

5行目はネメシスブロックのタイムスタンプを示しており、常に同じ値を表示します。

7行目は、UTC に変換された現在のネットワーク時間を示しています。

結論⚓︎

このチュートリアルでは、以下の方法を説明しました。

ステップ 関連ドキュメント
ネメシスタイムスタンプの取得 /network/properties GET
現在のネットワーク時間の取得 /node/time GET
両方の結合 (オプション)