Socket
Socket
Sign inDemoInstall

@foxglove/rostime

Package Overview
Dependencies
0
Maintainers
2
Versions
3
Alerts
File Explorer

Advanced tools

Install Socket

Detect and block malicious and high-risk dependencies

Install

Comparing version 1.1.0 to 1.1.1

12

dist/timeUtils.d.ts

@@ -23,2 +23,14 @@ import { Time } from "./Time";

/**
* Converts a Time to a string compatible with RFC3339/ISO8601. Similar to
* `toDate(stamp).toISOString()`, but with nanosecond precision.
* @param stamp Time to convert
*/
export declare function toRFC3339String(stamp: Time): string;
/**
* Parses a Time from a string compatible with a subset of ISO8601/RFC3339. Similar to
* `fromDate(new Date(string))`, but with nanosecond precision.
* @param stamp Time to convert
*/
export declare function fromRFC3339String(stamp: string): Time | undefined;
/**
* Convert a Time to a JavaScript Date object. NOTE: sub-millisecond precision is lost.

@@ -25,0 +37,0 @@ * @param stamp Time to convert

64

dist/timeUtils.js

@@ -15,3 +15,3 @@ "use strict";

Object.defineProperty(exports, "__esModule", { value: true });
exports.areEqual = exports.isGreaterThan = exports.isLessThan = exports.compare = exports.isTimeInRangeInclusive = exports.clampTime = exports.fromMicros = exports.fromMillis = exports.toMillis = exports.fromNanoSec = exports.fromSec = exports.toSec = exports.toMicroSec = exports.toNanoSec = exports.subtract = exports.add = exports.fixTime = exports.interpolate = exports.percentOf = exports.fromDate = exports.toDate = exports.fromString = exports.toString = exports.isTime = void 0;
exports.areEqual = exports.isGreaterThan = exports.isLessThan = exports.compare = exports.isTimeInRangeInclusive = exports.clampTime = exports.fromMicros = exports.fromMillis = exports.toMillis = exports.fromNanoSec = exports.fromSec = exports.toSec = exports.toMicroSec = exports.toNanoSec = exports.subtract = exports.add = exports.fixTime = exports.interpolate = exports.percentOf = exports.fromDate = exports.toDate = exports.fromRFC3339String = exports.toRFC3339String = exports.fromString = exports.toString = exports.isTime = void 0;
/**

@@ -47,2 +47,13 @@ * Test if a given object matches the signature of { sec: number; nsec: number }

/**
* Parse fractional seconds (digits following a decimal separator ".") and interpret them as an
* integer number of nanoseconds. Because of the rounding behavior, this function may return 1e9 (a
* value that would be too large for the `nsec` field).
*/
function parseNanoseconds(digits) {
// There can be 9 digits of nanoseconds. If the fractional part is "1", we need to add eight
// zeros. Also, make sure we round to an integer if we need to _remove_ digits.
const digitsShort = 9 - digits.length;
return Math.round(parseInt(digits, 10) * 10 ** digitsShort);
}
/**
* Converts a string containing floating point number of seconds to a Time. We use a string because

@@ -71,8 +82,5 @@ * nanosecond precision cannot be stored in a 64-bit float for large values (e.g. UNIX timestamps).

}
// There can be 9 digits of nanoseconds. If the fractional part is "1", we need to add eight
// zeros. Also, make sure we round to an integer if we need to _remove_ digits.
const digitsShort = 9 - second.length;
const nsec = Math.round(parseInt(second, 10) * 10 ** digitsShort);
// It's possible we rounded to { sec: 1, nsec: 1e9 }, which is invalid, so fixTime.
const sec = parseInt(first, 10);
const nsec = parseNanoseconds(second);
return fixTime({ sec: isNaN(sec) ? 0 : sec, nsec });

@@ -82,2 +90,48 @@ }

/**
* Converts a Time to a string compatible with RFC3339/ISO8601. Similar to
* `toDate(stamp).toISOString()`, but with nanosecond precision.
* @param stamp Time to convert
*/
function toRFC3339String(stamp) {
if (stamp.sec < 0 || stamp.nsec < 0) {
throw new Error(`Invalid negative time { sec: ${stamp.sec}, nsec: ${stamp.nsec} }`);
}
if (stamp.nsec >= 1e9) {
throw new Error(`Invalid nanosecond value ${stamp.nsec}`);
}
const date = new Date(stamp.sec * 1000);
const year = date.getUTCFullYear();
const month = (date.getUTCMonth() + 1).toFixed().padStart(2, "0");
const day = date.getUTCDate().toFixed().padStart(2, "0");
const hour = date.getUTCHours().toFixed().padStart(2, "0");
const minute = date.getUTCMinutes().toFixed().padStart(2, "0");
const second = date.getUTCSeconds().toFixed().padStart(2, "0");
const nanosecond = stamp.nsec.toFixed().padStart(9, "0");
return `${year}-${month}-${day}T${hour}:${minute}:${second}.${nanosecond}Z`;
}
exports.toRFC3339String = toRFC3339String;
/**
* Parses a Time from a string compatible with a subset of ISO8601/RFC3339. Similar to
* `fromDate(new Date(string))`, but with nanosecond precision.
* @param stamp Time to convert
*/
function fromRFC3339String(stamp) {
const match = /^(\d{4,})-(\d\d)-(\d\d)[Tt](\d\d):(\d\d):(\d\d)(?:\.(\d+))?(?:[Zz]|([+-])(\d\d):(\d\d))$/.exec(stamp);
if (match == null) {
return undefined;
}
const [, year, month, day, hour, minute, second, frac, plusMinus, offHours, offMinutes] = match;
const offSign = plusMinus === "-" ? -1 : 1;
const utcMillis = Date.UTC(+year, +month - 1, +day, +hour - offSign * +(offHours ?? 0), +minute - offSign * +(offMinutes ?? 0), +second);
if (utcMillis % 1000 !== 0) {
return undefined;
}
// It's possible we rounded to { sec: 1, nsec: 1e9 }, which is invalid, so fixTime.
return fixTime({
sec: utcMillis / 1000,
nsec: frac != undefined ? parseNanoseconds(frac) : 0,
});
}
exports.fromRFC3339String = fromRFC3339String;
/**
* Convert a Time to a JavaScript Date object. NOTE: sub-millisecond precision is lost.

@@ -84,0 +138,0 @@ * @param stamp Time to convert

231

dist/timeUtils.test.js

@@ -38,4 +38,102 @@ "use strict";

});
describe("toRFC3339String", () => {
it("formats whole values correctly", () => {
expect(rostime.toRFC3339String({ sec: 1, nsec: 0 })).toEqual("1970-01-01T00:00:01.000000000Z");
});
it("formats partial nanos", () => {
expect(rostime.toRFC3339String({ sec: 102, nsec: 304 })).toEqual("1970-01-01T00:01:42.000000304Z");
expect(rostime.toRFC3339String({ sec: 102, nsec: 99900000 })).toEqual("1970-01-01T00:01:42.099900000Z");
});
it("formats max nanos", () => {
expect(rostime.toRFC3339String({ sec: 102, nsec: 999999999 })).toEqual("1970-01-01T00:01:42.999999999Z");
});
it("does not format negative times", () => {
expect(() => rostime.toRFC3339String({ sec: -1, nsec: 0 })).toThrow();
});
it("interoperates with Date.parse", () => {
const date = new Date(Date.UTC(2021, 8, 29, 18, 12, 8, 123));
expect(Date.parse(rostime.toRFC3339String(rostime.fromDate(date)))).toEqual(+date);
});
});
describe("fromRFC3339String", () => {
it("parses nanosecond precision", () => {
expect(rostime.fromRFC3339String("1970-01-01T00:00:00.0000000Z")).toEqual({
sec: 0,
nsec: 0,
});
expect(rostime.fromRFC3339String("1970-01-01T00:00:00.000000001Z")).toEqual({
sec: 0,
nsec: 1,
});
expect(rostime.fromRFC3339String("1970-01-01T00:00:00.0000000001Z")).toEqual({
sec: 0,
nsec: 0,
});
expect(rostime.fromRFC3339String("1970-01-01T00:00:00.100000000Z")).toEqual({
sec: 0,
nsec: 100000000,
});
expect(rostime.fromRFC3339String("1970-01-01T00:00:00.0123Z")).toEqual({
sec: 0,
nsec: 12300000,
});
expect(rostime.fromRFC3339String("1970-01-01T00:00:00.012345678Z")).toEqual({
sec: 0,
nsec: 12345678,
});
});
it("rounds to nearest nanosecond", () => {
expect(rostime.fromRFC3339String("1970-01-01T00:00:00.0123456789Z")).toEqual({
sec: 0,
nsec: 12345679,
});
expect(rostime.fromRFC3339String("1970-01-01T00:00:00.9999999991Z")).toEqual({
sec: 0,
nsec: 999999999,
});
expect(rostime.fromRFC3339String("1970-01-01T00:00:00.9999999995Z")).toEqual({
sec: 1,
nsec: 0,
});
expect(rostime.fromRFC3339String("1970-01-01T00:00:01.9999999995Z")).toEqual({
sec: 2,
nsec: 0,
});
});
it("handles time zone offsets", () => {
expect(rostime.fromRFC3339String("2021-09-29T18:12:08.123456789+00:00")).toEqual({
sec: 1632939128,
nsec: 123456789,
});
expect(rostime.fromRFC3339String("2021-09-29T18:12:08.123456789-00:00")).toEqual({
sec: 1632939128,
nsec: 123456789,
});
expect(rostime.fromRFC3339String("2021-09-29T18:12:08.123456789+00:01")).toEqual({
sec: 1632939128 - 60,
nsec: 123456789,
});
expect(rostime.fromRFC3339String("2021-09-29T18:12:08.123456789-00:01")).toEqual({
sec: 1632939128 + 60,
nsec: 123456789,
});
expect(rostime.fromRFC3339String("2021-09-29T18:12:08.123456789+10:09")).toEqual({
sec: 1632939128 - (10 * 60 + 9) * 60,
nsec: 123456789,
});
expect(rostime.fromRFC3339String("2021-09-29T18:12:08.123456789-10:09")).toEqual({
sec: 1632939128 + (10 * 60 + 9) * 60,
nsec: 123456789,
});
});
it("interoperates with Date.toISOString", () => {
const date = new Date(Date.UTC(2021, 8, 29, 18, 12, 8, 123));
expect(rostime.fromRFC3339String(date.toISOString())).toEqual({
sec: 1632939128,
nsec: 123000000,
});
});
});
describe("toString", () => {
it("formats whole values correction", () => {
it("formats whole values correctly", () => {
expect(rostime.toString({ sec: 1, nsec: 0 })).toEqual("1.000000000");

@@ -171,2 +269,3 @@ });

};
// eslint-disable-next-line jest/expect-expect
it("can add two times together", () => {

@@ -194,45 +293,53 @@ testAddition({ sec: 0, nsec: 0 }, { sec: 0, nsec: 0 }, { sec: 0, nsec: 0 });

describe("subtractTimes", () => {
expect(rostime.subtract({ sec: 1, nsec: 1 }, { sec: 1, nsec: 1 })).toEqual({ sec: 0, nsec: 0 });
expect(rostime.subtract({ sec: 1, nsec: 2 }, { sec: 2, nsec: 1 })).toEqual({
sec: -1,
nsec: 1,
it("subtracts times", () => {
expect(rostime.subtract({ sec: 1, nsec: 1 }, { sec: 1, nsec: 1 })).toEqual({ sec: 0, nsec: 0 });
expect(rostime.subtract({ sec: 1, nsec: 2 }, { sec: 2, nsec: 1 })).toEqual({
sec: -1,
nsec: 1,
});
expect(rostime.subtract({ sec: 5, nsec: 100 }, { sec: 2, nsec: 10 })).toEqual({
sec: 3,
nsec: 90,
});
expect(rostime.subtract({ sec: 1, nsec: 1e8 }, { sec: 0, nsec: 5e8 })).toEqual({
sec: 0,
nsec: 600000000,
});
expect(rostime.subtract({ sec: 1, nsec: 0 }, { sec: 0, nsec: 1e9 - 1 })).toEqual({
sec: 0,
nsec: 1,
});
expect(rostime.subtract({ sec: 0, nsec: 0 }, { sec: 0, nsec: 1 })).toEqual({
sec: -1,
nsec: 1e9 - 1,
});
});
expect(rostime.subtract({ sec: 5, nsec: 100 }, { sec: 2, nsec: 10 })).toEqual({
sec: 3,
nsec: 90,
});
expect(rostime.subtract({ sec: 1, nsec: 1e8 }, { sec: 0, nsec: 5e8 })).toEqual({
sec: 0,
nsec: 600000000,
});
expect(rostime.subtract({ sec: 1, nsec: 0 }, { sec: 0, nsec: 1e9 - 1 })).toEqual({
sec: 0,
nsec: 1,
});
expect(rostime.subtract({ sec: 0, nsec: 0 }, { sec: 0, nsec: 1 })).toEqual({
sec: -1,
nsec: 1e9 - 1,
});
});
describe("toNanoSec", () => {
expect(rostime.toNanoSec({ sec: 0, nsec: 1 })).toEqual(1n);
expect(rostime.toNanoSec({ sec: 1, nsec: 0 })).toEqual(BigInt(1e9));
expect(rostime.toNanoSec({ sec: 1, nsec: 1 })).toEqual(BigInt(1e9) + 1n);
expect(rostime.toNanoSec({ sec: 2, nsec: 0 })).toEqual(BigInt(2e9));
expect(rostime.toNanoSec({ sec: 2, nsec: 1 })).toEqual(BigInt(2e9) + 1n);
it("works", () => {
expect(rostime.toNanoSec({ sec: 0, nsec: 1 })).toEqual(1n);
expect(rostime.toNanoSec({ sec: 1, nsec: 0 })).toEqual(BigInt(1e9));
expect(rostime.toNanoSec({ sec: 1, nsec: 1 })).toEqual(BigInt(1e9) + 1n);
expect(rostime.toNanoSec({ sec: 2, nsec: 0 })).toEqual(BigInt(2e9));
expect(rostime.toNanoSec({ sec: 2, nsec: 1 })).toEqual(BigInt(2e9) + 1n);
});
});
describe("toMicroSec", () => {
expect(rostime.toMicroSec({ sec: 0, nsec: 1 })).toEqual(0.001);
expect(rostime.toMicroSec({ sec: 0, nsec: 999 })).toEqual(0.999);
expect(rostime.toMicroSec({ sec: 0, nsec: 1000 })).toEqual(1);
expect(rostime.toMicroSec({ sec: 1, nsec: 0 })).toEqual(1e6);
expect(rostime.toMicroSec({ sec: 1, nsec: 1 })).toEqual(1000000.001);
expect(rostime.toMicroSec({ sec: 2, nsec: 0 })).toEqual(2e6);
expect(rostime.toMicroSec({ sec: 2, nsec: 1 })).toEqual(2000000.001);
it("works", () => {
expect(rostime.toMicroSec({ sec: 0, nsec: 1 })).toEqual(0.001);
expect(rostime.toMicroSec({ sec: 0, nsec: 999 })).toEqual(0.999);
expect(rostime.toMicroSec({ sec: 0, nsec: 1000 })).toEqual(1);
expect(rostime.toMicroSec({ sec: 1, nsec: 0 })).toEqual(1e6);
expect(rostime.toMicroSec({ sec: 1, nsec: 1 })).toEqual(1000000.001);
expect(rostime.toMicroSec({ sec: 2, nsec: 0 })).toEqual(2e6);
expect(rostime.toMicroSec({ sec: 2, nsec: 1 })).toEqual(2000000.001);
});
});
describe("toSec", () => {
expect(rostime.toSec({ sec: 1, nsec: 0 })).toBe(1);
expect(rostime.toSec({ sec: 1, nsec: 1 })).toBe(1.000000001);
expect(rostime.toSec({ sec: 1, nsec: 999999999 })).toBe(1.999999999);
expect(rostime.toSec({ sec: 1, nsec: 1000000000 })).toBe(2);
it("works", () => {
expect(rostime.toSec({ sec: 1, nsec: 0 })).toBe(1);
expect(rostime.toSec({ sec: 1, nsec: 1 })).toBe(1.000000001);
expect(rostime.toSec({ sec: 1, nsec: 999999999 })).toBe(1.999999999);
expect(rostime.toSec({ sec: 1, nsec: 1000000000 })).toBe(2);
});
});

@@ -258,27 +365,31 @@ describe("fromSec", () => {

describe("fromNanoSec", () => {
expect(rostime.fromNanoSec(0n)).toEqual({ sec: 0, nsec: 0 });
expect(rostime.fromNanoSec(1n)).toEqual({ sec: 0, nsec: 1 });
expect(rostime.fromNanoSec(10n)).toEqual({ sec: 0, nsec: 10 });
expect(rostime.fromNanoSec(BigInt(1e9))).toEqual({ sec: 1, nsec: 0 });
expect(rostime.fromNanoSec(BigInt(1e9) + 1n)).toEqual({ sec: 1, nsec: 1 });
expect(rostime.fromNanoSec(BigInt(2e9))).toEqual({ sec: 2, nsec: 0 });
expect(rostime.fromNanoSec(BigInt(2e9) + 1n)).toEqual({ sec: 2, nsec: 1 });
it("works", () => {
expect(rostime.fromNanoSec(0n)).toEqual({ sec: 0, nsec: 0 });
expect(rostime.fromNanoSec(1n)).toEqual({ sec: 0, nsec: 1 });
expect(rostime.fromNanoSec(10n)).toEqual({ sec: 0, nsec: 10 });
expect(rostime.fromNanoSec(BigInt(1e9))).toEqual({ sec: 1, nsec: 0 });
expect(rostime.fromNanoSec(BigInt(1e9) + 1n)).toEqual({ sec: 1, nsec: 1 });
expect(rostime.fromNanoSec(BigInt(2e9))).toEqual({ sec: 2, nsec: 0 });
expect(rostime.fromNanoSec(BigInt(2e9) + 1n)).toEqual({ sec: 2, nsec: 1 });
});
});
describe("toMillis", () => {
expect(rostime.toMillis({ sec: 0, nsec: 0 }, false)).toEqual(0);
expect(rostime.toMillis({ sec: 0, nsec: 0 }, true)).toEqual(0);
expect(rostime.toMillis({ sec: 0, nsec: 1 }, false)).toEqual(0);
expect(rostime.toMillis({ sec: 0, nsec: 1 }, true)).toEqual(1);
expect(rostime.toMillis({ sec: 0, nsec: 1e6 - 1 }, false)).toEqual(0);
expect(rostime.toMillis({ sec: 0, nsec: 1e6 - 1 }, true)).toEqual(1);
expect(rostime.toMillis({ sec: 0, nsec: 1e6 }, false)).toEqual(1);
expect(rostime.toMillis({ sec: 0, nsec: 1e6 }, true)).toEqual(1);
expect(rostime.toMillis({ sec: 1, nsec: 0 }, false)).toEqual(1000);
expect(rostime.toMillis({ sec: 1, nsec: 0 }, true)).toEqual(1000);
expect(rostime.toMillis({ sec: 1, nsec: 1 }, false)).toEqual(1000);
expect(rostime.toMillis({ sec: 1, nsec: 1 }, true)).toEqual(1001);
expect(rostime.toMillis({ sec: 2, nsec: 0 }, false)).toEqual(2000);
expect(rostime.toMillis({ sec: 2, nsec: 0 }, true)).toEqual(2000);
expect(rostime.toMillis({ sec: 2, nsec: 1 }, false)).toEqual(2000);
expect(rostime.toMillis({ sec: 2, nsec: 1 }, true)).toEqual(2001);
it("works", () => {
expect(rostime.toMillis({ sec: 0, nsec: 0 }, false)).toEqual(0);
expect(rostime.toMillis({ sec: 0, nsec: 0 }, true)).toEqual(0);
expect(rostime.toMillis({ sec: 0, nsec: 1 }, false)).toEqual(0);
expect(rostime.toMillis({ sec: 0, nsec: 1 }, true)).toEqual(1);
expect(rostime.toMillis({ sec: 0, nsec: 1e6 - 1 }, false)).toEqual(0);
expect(rostime.toMillis({ sec: 0, nsec: 1e6 - 1 }, true)).toEqual(1);
expect(rostime.toMillis({ sec: 0, nsec: 1e6 }, false)).toEqual(1);
expect(rostime.toMillis({ sec: 0, nsec: 1e6 }, true)).toEqual(1);
expect(rostime.toMillis({ sec: 1, nsec: 0 }, false)).toEqual(1000);
expect(rostime.toMillis({ sec: 1, nsec: 0 }, true)).toEqual(1000);
expect(rostime.toMillis({ sec: 1, nsec: 1 }, false)).toEqual(1000);
expect(rostime.toMillis({ sec: 1, nsec: 1 }, true)).toEqual(1001);
expect(rostime.toMillis({ sec: 2, nsec: 0 }, false)).toEqual(2000);
expect(rostime.toMillis({ sec: 2, nsec: 0 }, true)).toEqual(2000);
expect(rostime.toMillis({ sec: 2, nsec: 1 }, false)).toEqual(2000);
expect(rostime.toMillis({ sec: 2, nsec: 1 }, true)).toEqual(2001);
});
});

@@ -285,0 +396,0 @@ describe("fromMillis", () => {

{
"name": "@foxglove/rostime",
"version": "1.1.0",
"version": "1.1.1",
"description": "ROS (Robot Operating System) Time and Duration primitives and helper methods",

@@ -22,4 +22,4 @@ "license": "MIT",

"author": {
"name": "Foxglove Technologies",
"email": "support@foxglove.dev"
"name": "Foxglove Technologies Inc",
"email": "contact@foxglove.dev"
},

@@ -45,20 +45,21 @@ "homepage": "https://github.com/foxglove/rostime",

"devDependencies": {
"@foxglove/eslint-plugin": "0.10.0",
"@foxglove/eslint-plugin": "0.13.0",
"@types/eslint": "^7",
"@types/eslint-plugin-prettier": "^3",
"@types/jest": "^26.0.23",
"@types/prettier": "2.3.0",
"@typescript-eslint/eslint-plugin": "4.28.0",
"@typescript-eslint/parser": "4.28.0",
"esbuild": "0.12.9",
"@types/prettier": "2.3.2",
"@typescript-eslint/eslint-plugin": "4.28.3",
"@typescript-eslint/parser": "4.28.3",
"esbuild": "0.12.15",
"esbuild-jest": "0.5.0",
"eslint": "7.29.0",
"eslint": "7.31.0",
"eslint-config-prettier": "8.3.0",
"eslint-plugin-filenames": "^1.3.2",
"eslint-plugin-import": "2.23.4",
"eslint-plugin-jest": "24.3.6",
"eslint-plugin-prettier": "3.4.0",
"jest": "27.0.5",
"prettier": "2.3.1",
"typescript": "4.3.4"
"jest": "27.0.6",
"prettier": "2.3.2",
"typescript": "4.3.5"
}
}

@@ -28,1 +28,5 @@ # @foxglove/rostime

3. GitHub Actions will take care of the rest
## Stay in touch
Join our [Slack channel](https://foxglove.dev/join-slack) to ask questions, share feedback, and stay up to date on what our team is working on.

@@ -21,4 +21,117 @@ import * as rostime from ".";

describe("toRFC3339String", () => {
it("formats whole values correctly", () => {
expect(rostime.toRFC3339String({ sec: 1, nsec: 0 })).toEqual("1970-01-01T00:00:01.000000000Z");
});
it("formats partial nanos", () => {
expect(rostime.toRFC3339String({ sec: 102, nsec: 304 })).toEqual(
"1970-01-01T00:01:42.000000304Z",
);
expect(rostime.toRFC3339String({ sec: 102, nsec: 99900000 })).toEqual(
"1970-01-01T00:01:42.099900000Z",
);
});
it("formats max nanos", () => {
expect(rostime.toRFC3339String({ sec: 102, nsec: 999999999 })).toEqual(
"1970-01-01T00:01:42.999999999Z",
);
});
it("does not format negative times", () => {
expect(() => rostime.toRFC3339String({ sec: -1, nsec: 0 })).toThrow();
});
it("interoperates with Date.parse", () => {
const date = new Date(Date.UTC(2021, 8, 29, 18, 12, 8, 123));
expect(Date.parse(rostime.toRFC3339String(rostime.fromDate(date)))).toEqual(+date);
});
});
describe("fromRFC3339String", () => {
it("parses nanosecond precision", () => {
expect(rostime.fromRFC3339String("1970-01-01T00:00:00.0000000Z")).toEqual({
sec: 0,
nsec: 0,
});
expect(rostime.fromRFC3339String("1970-01-01T00:00:00.000000001Z")).toEqual({
sec: 0,
nsec: 1,
});
expect(rostime.fromRFC3339String("1970-01-01T00:00:00.0000000001Z")).toEqual({
sec: 0,
nsec: 0,
});
expect(rostime.fromRFC3339String("1970-01-01T00:00:00.100000000Z")).toEqual({
sec: 0,
nsec: 100_000_000,
});
expect(rostime.fromRFC3339String("1970-01-01T00:00:00.0123Z")).toEqual({
sec: 0,
nsec: 12_300_000,
});
expect(rostime.fromRFC3339String("1970-01-01T00:00:00.012345678Z")).toEqual({
sec: 0,
nsec: 12_345_678,
});
});
it("rounds to nearest nanosecond", () => {
expect(rostime.fromRFC3339String("1970-01-01T00:00:00.0123456789Z")).toEqual({
sec: 0,
nsec: 12_345_679,
});
expect(rostime.fromRFC3339String("1970-01-01T00:00:00.9999999991Z")).toEqual({
sec: 0,
nsec: 999_999_999,
});
expect(rostime.fromRFC3339String("1970-01-01T00:00:00.9999999995Z")).toEqual({
sec: 1,
nsec: 0,
});
expect(rostime.fromRFC3339String("1970-01-01T00:00:01.9999999995Z")).toEqual({
sec: 2,
nsec: 0,
});
});
it("handles time zone offsets", () => {
expect(rostime.fromRFC3339String("2021-09-29T18:12:08.123456789+00:00")).toEqual({
sec: 1632939128,
nsec: 123_456_789,
});
expect(rostime.fromRFC3339String("2021-09-29T18:12:08.123456789-00:00")).toEqual({
sec: 1632939128,
nsec: 123_456_789,
});
expect(rostime.fromRFC3339String("2021-09-29T18:12:08.123456789+00:01")).toEqual({
sec: 1632939128 - 60,
nsec: 123_456_789,
});
expect(rostime.fromRFC3339String("2021-09-29T18:12:08.123456789-00:01")).toEqual({
sec: 1632939128 + 60,
nsec: 123_456_789,
});
expect(rostime.fromRFC3339String("2021-09-29T18:12:08.123456789+10:09")).toEqual({
sec: 1632939128 - (10 * 60 + 9) * 60,
nsec: 123_456_789,
});
expect(rostime.fromRFC3339String("2021-09-29T18:12:08.123456789-10:09")).toEqual({
sec: 1632939128 + (10 * 60 + 9) * 60,
nsec: 123_456_789,
});
});
it("interoperates with Date.toISOString", () => {
const date = new Date(Date.UTC(2021, 8, 29, 18, 12, 8, 123));
expect(rostime.fromRFC3339String(date.toISOString())).toEqual({
sec: 1632939128,
nsec: 123_000_000,
});
});
});
describe("toString", () => {
it("formats whole values correction", () => {
it("formats whole values correctly", () => {
expect(rostime.toString({ sec: 1, nsec: 0 })).toEqual("1.000000000");

@@ -172,2 +285,3 @@ });

// eslint-disable-next-line jest/expect-expect
it("can add two times together", () => {

@@ -197,48 +311,56 @@ testAddition({ sec: 0, nsec: 0 }, { sec: 0, nsec: 0 }, { sec: 0, nsec: 0 });

describe("subtractTimes", () => {
expect(rostime.subtract({ sec: 1, nsec: 1 }, { sec: 1, nsec: 1 })).toEqual({ sec: 0, nsec: 0 });
expect(rostime.subtract({ sec: 1, nsec: 2 }, { sec: 2, nsec: 1 })).toEqual({
sec: -1,
nsec: 1,
it("subtracts times", () => {
expect(rostime.subtract({ sec: 1, nsec: 1 }, { sec: 1, nsec: 1 })).toEqual({ sec: 0, nsec: 0 });
expect(rostime.subtract({ sec: 1, nsec: 2 }, { sec: 2, nsec: 1 })).toEqual({
sec: -1,
nsec: 1,
});
expect(rostime.subtract({ sec: 5, nsec: 100 }, { sec: 2, nsec: 10 })).toEqual({
sec: 3,
nsec: 90,
});
expect(rostime.subtract({ sec: 1, nsec: 1e8 }, { sec: 0, nsec: 5e8 })).toEqual({
sec: 0,
nsec: 600000000,
});
expect(rostime.subtract({ sec: 1, nsec: 0 }, { sec: 0, nsec: 1e9 - 1 })).toEqual({
sec: 0,
nsec: 1,
});
expect(rostime.subtract({ sec: 0, nsec: 0 }, { sec: 0, nsec: 1 })).toEqual({
sec: -1,
nsec: 1e9 - 1,
});
});
expect(rostime.subtract({ sec: 5, nsec: 100 }, { sec: 2, nsec: 10 })).toEqual({
sec: 3,
nsec: 90,
});
expect(rostime.subtract({ sec: 1, nsec: 1e8 }, { sec: 0, nsec: 5e8 })).toEqual({
sec: 0,
nsec: 600000000,
});
expect(rostime.subtract({ sec: 1, nsec: 0 }, { sec: 0, nsec: 1e9 - 1 })).toEqual({
sec: 0,
nsec: 1,
});
expect(rostime.subtract({ sec: 0, nsec: 0 }, { sec: 0, nsec: 1 })).toEqual({
sec: -1,
nsec: 1e9 - 1,
});
});
describe("toNanoSec", () => {
expect(rostime.toNanoSec({ sec: 0, nsec: 1 })).toEqual(1n);
expect(rostime.toNanoSec({ sec: 1, nsec: 0 })).toEqual(BigInt(1e9));
expect(rostime.toNanoSec({ sec: 1, nsec: 1 })).toEqual(BigInt(1e9) + 1n);
expect(rostime.toNanoSec({ sec: 2, nsec: 0 })).toEqual(BigInt(2e9));
expect(rostime.toNanoSec({ sec: 2, nsec: 1 })).toEqual(BigInt(2e9) + 1n);
it("works", () => {
expect(rostime.toNanoSec({ sec: 0, nsec: 1 })).toEqual(1n);
expect(rostime.toNanoSec({ sec: 1, nsec: 0 })).toEqual(BigInt(1e9));
expect(rostime.toNanoSec({ sec: 1, nsec: 1 })).toEqual(BigInt(1e9) + 1n);
expect(rostime.toNanoSec({ sec: 2, nsec: 0 })).toEqual(BigInt(2e9));
expect(rostime.toNanoSec({ sec: 2, nsec: 1 })).toEqual(BigInt(2e9) + 1n);
});
});
describe("toMicroSec", () => {
expect(rostime.toMicroSec({ sec: 0, nsec: 1 })).toEqual(0.001);
expect(rostime.toMicroSec({ sec: 0, nsec: 999 })).toEqual(0.999);
expect(rostime.toMicroSec({ sec: 0, nsec: 1000 })).toEqual(1);
expect(rostime.toMicroSec({ sec: 1, nsec: 0 })).toEqual(1e6);
expect(rostime.toMicroSec({ sec: 1, nsec: 1 })).toEqual(1000000.001);
expect(rostime.toMicroSec({ sec: 2, nsec: 0 })).toEqual(2e6);
expect(rostime.toMicroSec({ sec: 2, nsec: 1 })).toEqual(2000000.001);
it("works", () => {
expect(rostime.toMicroSec({ sec: 0, nsec: 1 })).toEqual(0.001);
expect(rostime.toMicroSec({ sec: 0, nsec: 999 })).toEqual(0.999);
expect(rostime.toMicroSec({ sec: 0, nsec: 1000 })).toEqual(1);
expect(rostime.toMicroSec({ sec: 1, nsec: 0 })).toEqual(1e6);
expect(rostime.toMicroSec({ sec: 1, nsec: 1 })).toEqual(1000000.001);
expect(rostime.toMicroSec({ sec: 2, nsec: 0 })).toEqual(2e6);
expect(rostime.toMicroSec({ sec: 2, nsec: 1 })).toEqual(2000000.001);
});
});
describe("toSec", () => {
expect(rostime.toSec({ sec: 1, nsec: 0 })).toBe(1);
expect(rostime.toSec({ sec: 1, nsec: 1 })).toBe(1.000000001);
expect(rostime.toSec({ sec: 1, nsec: 999999999 })).toBe(1.999999999);
expect(rostime.toSec({ sec: 1, nsec: 1000000000 })).toBe(2);
it("works", () => {
expect(rostime.toSec({ sec: 1, nsec: 0 })).toBe(1);
expect(rostime.toSec({ sec: 1, nsec: 1 })).toBe(1.000000001);
expect(rostime.toSec({ sec: 1, nsec: 999999999 })).toBe(1.999999999);
expect(rostime.toSec({ sec: 1, nsec: 1000000000 })).toBe(2);
});
});

@@ -267,28 +389,32 @@

describe("fromNanoSec", () => {
expect(rostime.fromNanoSec(0n)).toEqual({ sec: 0, nsec: 0 });
expect(rostime.fromNanoSec(1n)).toEqual({ sec: 0, nsec: 1 });
expect(rostime.fromNanoSec(10n)).toEqual({ sec: 0, nsec: 10 });
expect(rostime.fromNanoSec(BigInt(1e9))).toEqual({ sec: 1, nsec: 0 });
expect(rostime.fromNanoSec(BigInt(1e9) + 1n)).toEqual({ sec: 1, nsec: 1 });
expect(rostime.fromNanoSec(BigInt(2e9))).toEqual({ sec: 2, nsec: 0 });
expect(rostime.fromNanoSec(BigInt(2e9) + 1n)).toEqual({ sec: 2, nsec: 1 });
it("works", () => {
expect(rostime.fromNanoSec(0n)).toEqual({ sec: 0, nsec: 0 });
expect(rostime.fromNanoSec(1n)).toEqual({ sec: 0, nsec: 1 });
expect(rostime.fromNanoSec(10n)).toEqual({ sec: 0, nsec: 10 });
expect(rostime.fromNanoSec(BigInt(1e9))).toEqual({ sec: 1, nsec: 0 });
expect(rostime.fromNanoSec(BigInt(1e9) + 1n)).toEqual({ sec: 1, nsec: 1 });
expect(rostime.fromNanoSec(BigInt(2e9))).toEqual({ sec: 2, nsec: 0 });
expect(rostime.fromNanoSec(BigInt(2e9) + 1n)).toEqual({ sec: 2, nsec: 1 });
});
});
describe("toMillis", () => {
expect(rostime.toMillis({ sec: 0, nsec: 0 }, false)).toEqual(0);
expect(rostime.toMillis({ sec: 0, nsec: 0 }, true)).toEqual(0);
expect(rostime.toMillis({ sec: 0, nsec: 1 }, false)).toEqual(0);
expect(rostime.toMillis({ sec: 0, nsec: 1 }, true)).toEqual(1);
expect(rostime.toMillis({ sec: 0, nsec: 1e6 - 1 }, false)).toEqual(0);
expect(rostime.toMillis({ sec: 0, nsec: 1e6 - 1 }, true)).toEqual(1);
expect(rostime.toMillis({ sec: 0, nsec: 1e6 }, false)).toEqual(1);
expect(rostime.toMillis({ sec: 0, nsec: 1e6 }, true)).toEqual(1);
expect(rostime.toMillis({ sec: 1, nsec: 0 }, false)).toEqual(1000);
expect(rostime.toMillis({ sec: 1, nsec: 0 }, true)).toEqual(1000);
expect(rostime.toMillis({ sec: 1, nsec: 1 }, false)).toEqual(1000);
expect(rostime.toMillis({ sec: 1, nsec: 1 }, true)).toEqual(1001);
expect(rostime.toMillis({ sec: 2, nsec: 0 }, false)).toEqual(2000);
expect(rostime.toMillis({ sec: 2, nsec: 0 }, true)).toEqual(2000);
expect(rostime.toMillis({ sec: 2, nsec: 1 }, false)).toEqual(2000);
expect(rostime.toMillis({ sec: 2, nsec: 1 }, true)).toEqual(2001);
it("works", () => {
expect(rostime.toMillis({ sec: 0, nsec: 0 }, false)).toEqual(0);
expect(rostime.toMillis({ sec: 0, nsec: 0 }, true)).toEqual(0);
expect(rostime.toMillis({ sec: 0, nsec: 1 }, false)).toEqual(0);
expect(rostime.toMillis({ sec: 0, nsec: 1 }, true)).toEqual(1);
expect(rostime.toMillis({ sec: 0, nsec: 1e6 - 1 }, false)).toEqual(0);
expect(rostime.toMillis({ sec: 0, nsec: 1e6 - 1 }, true)).toEqual(1);
expect(rostime.toMillis({ sec: 0, nsec: 1e6 }, false)).toEqual(1);
expect(rostime.toMillis({ sec: 0, nsec: 1e6 }, true)).toEqual(1);
expect(rostime.toMillis({ sec: 1, nsec: 0 }, false)).toEqual(1000);
expect(rostime.toMillis({ sec: 1, nsec: 0 }, true)).toEqual(1000);
expect(rostime.toMillis({ sec: 1, nsec: 1 }, false)).toEqual(1000);
expect(rostime.toMillis({ sec: 1, nsec: 1 }, true)).toEqual(1001);
expect(rostime.toMillis({ sec: 2, nsec: 0 }, false)).toEqual(2000);
expect(rostime.toMillis({ sec: 2, nsec: 0 }, true)).toEqual(2000);
expect(rostime.toMillis({ sec: 2, nsec: 1 }, false)).toEqual(2000);
expect(rostime.toMillis({ sec: 2, nsec: 1 }, true)).toEqual(2001);
});
});

@@ -295,0 +421,0 @@

@@ -49,2 +49,14 @@ // This Source Code Form is subject to the terms of the Mozilla Public

/**
* Parse fractional seconds (digits following a decimal separator ".") and interpret them as an
* integer number of nanoseconds. Because of the rounding behavior, this function may return 1e9 (a
* value that would be too large for the `nsec` field).
*/
function parseNanoseconds(digits: string) {
// There can be 9 digits of nanoseconds. If the fractional part is "1", we need to add eight
// zeros. Also, make sure we round to an integer if we need to _remove_ digits.
const digitsShort = 9 - digits.length;
return Math.round(parseInt(digits, 10) * 10 ** digitsShort);
}
/**
* Converts a string containing floating point number of seconds to a Time. We use a string because

@@ -75,8 +87,5 @@ * nanosecond precision cannot be stored in a 64-bit float for large values (e.g. UNIX timestamps).

// There can be 9 digits of nanoseconds. If the fractional part is "1", we need to add eight
// zeros. Also, make sure we round to an integer if we need to _remove_ digits.
const digitsShort = 9 - second.length;
const nsec = Math.round(parseInt(second, 10) * 10 ** digitsShort);
// It's possible we rounded to { sec: 1, nsec: 1e9 }, which is invalid, so fixTime.
const sec = parseInt(first, 10);
const nsec = parseNanoseconds(second);
return fixTime({ sec: isNaN(sec) ? 0 : sec, nsec });

@@ -86,2 +95,58 @@ }

/**
* Converts a Time to a string compatible with RFC3339/ISO8601. Similar to
* `toDate(stamp).toISOString()`, but with nanosecond precision.
* @param stamp Time to convert
*/
export function toRFC3339String(stamp: Time): string {
if (stamp.sec < 0 || stamp.nsec < 0) {
throw new Error(`Invalid negative time { sec: ${stamp.sec}, nsec: ${stamp.nsec} }`);
}
if (stamp.nsec >= 1e9) {
throw new Error(`Invalid nanosecond value ${stamp.nsec}`);
}
const date = new Date(stamp.sec * 1000);
const year = date.getUTCFullYear();
const month = (date.getUTCMonth() + 1).toFixed().padStart(2, "0");
const day = date.getUTCDate().toFixed().padStart(2, "0");
const hour = date.getUTCHours().toFixed().padStart(2, "0");
const minute = date.getUTCMinutes().toFixed().padStart(2, "0");
const second = date.getUTCSeconds().toFixed().padStart(2, "0");
const nanosecond = stamp.nsec.toFixed().padStart(9, "0");
return `${year}-${month}-${day}T${hour}:${minute}:${second}.${nanosecond}Z`;
}
/**
* Parses a Time from a string compatible with a subset of ISO8601/RFC3339. Similar to
* `fromDate(new Date(string))`, but with nanosecond precision.
* @param stamp Time to convert
*/
export function fromRFC3339String(stamp: string): Time | undefined {
const match =
/^(\d{4,})-(\d\d)-(\d\d)[Tt](\d\d):(\d\d):(\d\d)(?:\.(\d+))?(?:[Zz]|([+-])(\d\d):(\d\d))$/.exec(
stamp,
);
if (match == null) {
return undefined;
}
const [, year, month, day, hour, minute, second, frac, plusMinus, offHours, offMinutes] = match;
const offSign = plusMinus === "-" ? -1 : 1;
const utcMillis = Date.UTC(
+year!,
+month! - 1,
+day!,
+hour! - offSign * +(offHours ?? 0),
+minute! - offSign * +(offMinutes ?? 0),
+second!,
);
if (utcMillis % 1000 !== 0) {
return undefined;
}
// It's possible we rounded to { sec: 1, nsec: 1e9 }, which is invalid, so fixTime.
return fixTime({
sec: utcMillis / 1000,
nsec: frac != undefined ? parseNanoseconds(frac) : 0,
});
}
/**
* Convert a Time to a JavaScript Date object. NOTE: sub-millisecond precision is lost.

@@ -88,0 +153,0 @@ * @param stamp Time to convert

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc