Skip to content

Network Time⚓︎

Time in the Symbol blockchain is measured in network time, defined as the number of milliseconds elapsed since the chain's first block.

This quick tutorial shows how to convert the network time returned by API calls to standard timestamps like UTC.

Prerequisites⚓︎

This tutorial uses the Symbol REST API without requiring an SDK. You only need a way to make HTTP requests.

Full Code⚓︎

import json
import os
import datetime
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 = parseInt(
        timeJson.communicationTimestamps.receiveTimestamp, 10);

    const networkDatetime = new Date(
        nemesisDatetime.getTime() + 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

Code Explanation⚓︎

The code retrieves the Nemesis block timestamp and the current network time. Then it adds them together to obtain the current time in standard UTC.

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)
    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);

The Nemesis block creation time is a fixed network property and can be retrieved using the /network/properties GET endpoint. The returned value (epochAdjustment), after removing the s suffix and converting it to an integer, is a UNIX timestamp. That is, the number of non-leap seconds that have elapsed since the Unix epoch (00:00:00 UTC on 1 January 1970).

This tutorial retrieves it from the network for illustration purposes, but this is a fixed value and can be treated as a constant and hardcoded if desired. For Symbol's main network, the value is 1615853185 which corresponds to 2021-03-16T00:06:25Z (March 16, 2021 at 12:06:25 AM UTC).

Fetch Current Network Time⚓︎

    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 = parseInt(
        timeJson.communicationTimestamps.receiveTimestamp, 10);

The current network time, as understood by the queried node, is obtained using the /node/time GET endpoint. Nodes in the network are typically synchronized, so they return similar times.

The actual property queried is communicationTimestamps.receiveTimestamp, which represents the time at which the request was received by the node.

This value is expressed in network time, that is, milliseconds elapsed since the Nemesis block was created.

Conversion⚓︎

The conversion from the current network time to UTC only requires adding the two numbers together, taking care to use consistent units (seconds or milliseconds).

    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() + networkMs);

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

This tutorial makes the addition manually to show the process. If you are using the Symbol SDK, provides a more convenient abstraction.

Output⚓︎

The output shown below corresponds to a typical run of the program.

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

Line 5 shows the Nemesis block timestamp, and always displays the same value.

Line 7 shows the current network time converted to UTC.

Conclusion⚓︎

This tutorial showed how to:

Step Related documentation
Obtain the Nemesis timestamp /network/properties GET
Obtain the current network time /node/time GET
Combine both (optional)