Add Timestamp renderer

This commit is contained in:
Simon Warta 2022-07-14 13:28:02 +02:00
parent fc6a954717
commit 944fb9de68
3 changed files with 72 additions and 1 deletions

View File

@ -0,0 +1,21 @@
[
[0, 0, "1970-01-01T00:00:00.000000000Z"],
[0, 1, "1970-01-01T00:00:00.000000001Z"],
[0, 10, "1970-01-01T00:00:00.000000010Z"],
[0, 100, "1970-01-01T00:00:00.000000100Z"],
[0, 1000, "1970-01-01T00:00:00.000001000Z"],
[0, 10000, "1970-01-01T00:00:00.000010000Z"],
[0, 100000, "1970-01-01T00:00:00.000100000Z"],
[0, 1000000, "1970-01-01T00:00:00.001000000Z"],
[0, 10000000, "1970-01-01T00:00:00.010000000Z"],
[0, 100000000, "1970-01-01T00:00:00.100000000Z"],
[0, 999999999, "1970-01-01T00:00:00.999999999Z"],
[0, 1000000000, null],
[1, 0, "1970-01-01T00:00:01.000000000Z"],
[2, 0, "1970-01-01T00:00:02.000000000Z"],
[2, 6, "1970-01-01T00:00:02.000000006Z"],
[2, 6, "1970-01-01T00:00:02.000000006Z"],
[1657797740, 983000000, "2022-07-14T11:22:20.983000000Z"],
[-1, 0, "1969-12-31T23:59:59.000000000Z"],
[0, -1, null]
]

View File

@ -6,6 +6,7 @@ import coinData from "../testdata/coin.json";
import coinsData from "../testdata/coins.json"; import coinsData from "../testdata/coins.json";
import decimals from "../testdata/decimals.json"; import decimals from "../testdata/decimals.json";
import integers from "../testdata/integers.json"; import integers from "../testdata/integers.json";
import timestampData from "../testdata/timestamp.json";
import { import {
DisplayUnit, DisplayUnit,
formatBytes, formatBytes,
@ -13,12 +14,15 @@ import {
formatCoins, formatCoins,
formatDecimal, formatDecimal,
formatInteger, formatInteger,
formatTimestamp,
} from "./valuerenderers"; } from "./valuerenderers";
type TestDataCoin = Array<[Coin, DisplayUnit, string]>; type TestDataCoin = Array<[Coin, DisplayUnit, string]>;
type TestDataCoins = Array<[Coin[], Record<string, DisplayUnit>, string]>; type TestDataCoins = Array<[Coin[], Record<string, DisplayUnit>, string]>;
/** First argument is an array of bytes (0-255), second argument is the expected string */ /** First argument is an array of bytes (0-255), second argument is the expected string */
type TestDataBytes = Array<[number[], string]>; type TestDataBytes = Array<[number[], string]>;
/** First argument is seconds, second argument is nanor and third argument is the expected string or null on error */
type TestDataTimestamp = Array<[number, number, string | null]>;
describe("valuerenderers", () => { describe("valuerenderers", () => {
describe("formatInteger", () => { describe("formatInteger", () => {
@ -90,4 +94,25 @@ describe("valuerenderers", () => {
} }
}); });
}); });
describe("formatTimestamp", () => {
it("works", () => {
expect(formatTimestamp(0, 0)).toEqual("1970-01-01T00:00:00.000000000Z");
expect(formatTimestamp(0, 1)).toEqual("1970-01-01T00:00:00.000000001Z");
expect(formatTimestamp(1, 2)).toEqual("1970-01-01T00:00:01.000000002Z");
expect(formatTimestamp(5, 0)).toEqual("1970-01-01T00:00:05.000000000Z");
for (const [seconds, nanos, expected] of timestampData as TestDataTimestamp) {
if (expected === null) {
expect(() => formatTimestamp(seconds, nanos))
.withContext(`Input seconds=${seconds}, nanos=${nanos}`)
.toThrowError();
} else {
expect(formatTimestamp(seconds, nanos))
.withContext(`Input seconds=${seconds}, nanos=${nanos}`)
.toEqual(expected);
}
}
});
});
}); });

View File

@ -1,5 +1,5 @@
import { toBase64 } from "@cosmjs/encoding"; import { toBase64 } from "@cosmjs/encoding";
import { Decimal } from "@cosmjs/math"; import { Decimal, Uint32 } from "@cosmjs/math";
import { DenomUnit } from "cosmjs-types/cosmos/bank/v1beta1/bank"; import { DenomUnit } from "cosmjs-types/cosmos/bank/v1beta1/bank";
import { Coin } from "cosmjs-types/cosmos/base/v1beta1/coin"; import { Coin } from "cosmjs-types/cosmos/base/v1beta1/coin";
@ -40,3 +40,28 @@ export function formatCoins(input: Coin[], units: Record<string, DisplayUnit>):
export function formatBytes(data: Uint8Array): string { export function formatBytes(data: Uint8Array): string {
return toBase64(data); return toBase64(data);
} }
export interface DateWithNanoseconds extends Date {
/** Nanoseconds after the time stored in a vanilla Date (millisecond granularity) */
nanoseconds?: number;
}
function toRfc3339WithNanoseconds(dateTime: DateWithNanoseconds): string {
const millisecondIso = dateTime.toISOString();
const nanoseconds = dateTime.nanoseconds?.toString() ?? "";
return `${millisecondIso.slice(0, -1)}${nanoseconds.padStart(6, "0")}Z`;
}
function fromSeconds(seconds: number, nanos = 0): DateWithNanoseconds {
const checkedNanos = new Uint32(nanos).toNumber();
if (checkedNanos > 999_999_999) {
throw new Error("Nano seconds must not exceed 999999999");
}
const out: DateWithNanoseconds = new Date(seconds * 1000 + Math.floor(checkedNanos / 1000000));
out.nanoseconds = checkedNanos % 1000000;
return out;
}
export function formatTimestamp(seconds: number, nanos: number): string {
return toRfc3339WithNanoseconds(fromSeconds(seconds, nanos));
}