diff --git a/package.json b/package.json index 07fd92a..2f73768 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "miniseed", - "version": "0.1.6", + "version": "0.2.0", "type": "module", "scripts": { "build": "vite build", diff --git a/readme.md b/readme.md index c70d40a..d6d6d54 100644 --- a/readme.md +++ b/readme.md @@ -20,6 +20,7 @@ Typescript definitions are included. import { serialiseToMiniSEEDBuffer, serialiseToMiniSEEDUint8Array, + startTimeFromDate, Flags, } from "miniseed"; @@ -27,7 +28,18 @@ const data = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]; const metadata = { sourceIdentifier: "FDSN:__...", // An FDSN ID: http://docs.fdsn.org/projects/source-identifiers/ - startTime: new Date(), // Or you can manually specify nanoseconds, seconds, etc + // You can manually specify nanoseconds, seconds, etc + startTime: { + year: 1978, + dayOfYear: 264, + hour: 21, + minute: 0, + second: 0, // This needs to be an integer + // There's no milliseconds field - they're included in nanoSecond + nanoSecond: 0, + }, + // Or use a helper function to convert from a Date object + startTime: startTimeFromDate(new Date()), encoding: "Int32", // Or text, Int16, Float32, Float64 // All other metadata fields are optional, but supported. diff --git a/src/miniseed.ts b/src/miniseed.ts index d5ec66f..aa7d81a 100644 --- a/src/miniseed.ts +++ b/src/miniseed.ts @@ -16,6 +16,15 @@ export const Flags = { CLOCK_LOCKED: 0b00100000, }; +type StartTimeData = { + nanoSecond: number; + year: number; + dayOfYear: number; + hour: number; + minute: number; + second: number; +}; + type Metadata = { flags?: number; encoding?: T; @@ -23,16 +32,7 @@ type Metadata = { dataPublicationVersion?: number; extraHeaderFields?: any; sourceIdentifier: string; - startTime: - | Date - | { - nanoSecond: number; - year: number; - dayOfYear: number; - hour: number; - minute: number; - second: number; - }; + startTime: StartTimeData; }; const metadataDefaults = { @@ -43,6 +43,22 @@ const metadataDefaults = { extraHeaderFields: undefined, }; +export function startTimeFromDate(date: Date): StartTimeData { + if (isNaN(date.getTime())) + throw new Error("Invalid Date provided for metadata.timestamp"); + + return { + year: date.getFullYear(), + dayOfYear: getDayOfYear(date), + hour: date.getHours(), + minute: date.getMinutes(), + second: date.getSeconds(), + // This isn't super precise. + // One day we may get Temporal and all will be well. + nanoSecond: date.getMilliseconds() * 1000000, + }; +} + /** * Serialises an array of numbers to miniSEED v3 format, * that can then be written to a file. @@ -55,21 +71,6 @@ export function serialiseToMiniSEEDBuffer( ): ArrayBuffer { let metadata = { ...metadataDefaults, ..._metadata }; - if (metadata.startTime instanceof Date) { - if (isNaN(metadata.startTime.getTime())) - throw new Error("Invalid Date provided for metadata.timestamp"); - metadata.startTime = { - year: metadata.startTime.getFullYear(), - dayOfYear: getDayOfYear(metadata.startTime), - hour: metadata.startTime.getHours(), - minute: metadata.startTime.getMinutes(), - second: metadata.startTime.getSeconds(), - // This isn't super precise. - // One day we may get Temporal and all will be well. - nanoSecond: metadata.startTime.getMilliseconds() * 1000000, - }; - } - const encodingInfo = encodingTypes[metadata.encoding]; let encodedText: Uint8Array; diff --git a/tests/date-utils.test.ts b/tests/date-utils.test.ts new file mode 100644 index 0000000..2b54da3 --- /dev/null +++ b/tests/date-utils.test.ts @@ -0,0 +1,39 @@ +import { describe, it, expect } from "vitest"; +import { startTimeFromDate } from "../src/miniseed"; + +describe("Date utilities", () => { + it("Works with a set date", () => { + const date = new Date("21 September 1978 21:45:30"); + const startTime = startTimeFromDate(date); + expect(startTime.year).toEqual(1978); + expect(startTime.dayOfYear).toEqual(264); + expect(startTime.hour).toEqual(21); + expect(startTime.minute).toEqual(45); + expect(startTime.second).toEqual(30); + expect(startTime.nanoSecond).toEqual(0); + }); + it("Handles milliseconds correctly", () => { + const date = new Date("21 September 1978"); + date.setMilliseconds(500); + const startTime = startTimeFromDate(date); + expect(startTime.nanoSecond).toEqual(500_000_000); + }); + it("Handles day-of-year edge cases", () => { + const date1 = new Date("29 February 2016"); + const startTime1 = startTimeFromDate(date1); + expect(startTime1.dayOfYear).toEqual(60); + + const date2 = new Date("1 March 2016"); + const startTime2 = startTimeFromDate(date2); + expect(startTime2.dayOfYear).toEqual(61); + }); + it("Handles day-of-year edge cases", () => { + const date1 = new Date("29 February 2016"); + const startTime1 = startTimeFromDate(date1); + expect(startTime1.dayOfYear).toEqual(60); + + const date2 = new Date("1 March 2016"); + const startTime2 = startTimeFromDate(date2); + expect(startTime2.dayOfYear).toEqual(61); + }); +}); diff --git a/tests/serialise.test.ts b/tests/serialise.test.ts index b8368e7..56e3cbb 100644 --- a/tests/serialise.test.ts +++ b/tests/serialise.test.ts @@ -2,6 +2,7 @@ import { describe, it, expect } from "vitest"; import { serialiseToMiniSEEDBuffer, serialiseToMiniSEEDUint8Array, + startTimeFromDate, } from "../src/miniseed"; import jDataView from "z-jdataview-temp-publish"; @@ -9,14 +10,14 @@ describe("Size", () => { it("Works with no data", () => { const serialised = serialiseToMiniSEEDUint8Array([], { sourceIdentifier: "", - startTime: new Date(), + startTime: startTimeFromDate(new Date()), }); expect(serialised.length).toEqual(40); }); it("Works with complex identifiers", () => { const serialised = serialiseToMiniSEEDUint8Array([], { sourceIdentifier: "🔥🦊", - startTime: new Date(), + startTime: startTimeFromDate(new Date()), }); expect(serialised.length).toEqual(48); }); @@ -56,7 +57,7 @@ describe("Size", () => { ], }, }, - startTime: new Date(), + startTime: startTimeFromDate(new Date()), }); expect(serialised.length).toEqual(517); }); @@ -66,7 +67,7 @@ describe("Basic serialisation", () => { it("Works with basic text", () => { const serialised = serialiseToMiniSEEDBuffer("beans", { sourceIdentifier: "", - startTime: new Date(), + startTime: startTimeFromDate(new Date()), encoding: "text", }).slice(40); @@ -76,7 +77,7 @@ describe("Basic serialisation", () => { it("Works with basic numbers", () => { const serialised = serialiseToMiniSEEDBuffer([1, 2, 3, 4, 5], { sourceIdentifier: "", - startTime: new Date(), + startTime: startTimeFromDate(new Date()), encoding: "Int32", }).slice(40); diff --git a/tests/vaid-files.test.ts b/tests/vaid-files.test.ts index 6302d66..85a82aa 100644 --- a/tests/vaid-files.test.ts +++ b/tests/vaid-files.test.ts @@ -3,7 +3,10 @@ import { promisify } from "node:util"; import { writeFile, mkdir, rm } from "node:fs/promises"; import { join } from "node:path"; import { describe, it, expect, beforeAll, afterAll } from "vitest"; -import { serialiseToMiniSEEDUint8Array } from "../src/miniseed"; +import { + serialiseToMiniSEEDUint8Array, + startTimeFromDate, +} from "../src/miniseed"; const exec = promisify(_exec); @@ -49,7 +52,7 @@ describe("Basic data validity", () => { const data = [1, 2, 3, 4, 5, 6, 7, 8]; const serialised = serialiseToMiniSEEDUint8Array(data, { sourceIdentifier: "https://zade.viggers.net/example", - startTime: new Date(), + startTime: startTimeFromDate(new Date()), }); await checkData(serialised, "1-2-3"); }); @@ -59,7 +62,7 @@ describe("Date validity", () => { it("Works with a 0 date", async () => { const serialised = serialiseToMiniSEEDUint8Array([], { sourceIdentifier: "https://zade.viggers.net/example", - startTime: new Date(0), + startTime: startTimeFromDate(new Date(0)), }); await checkData(serialised, "date-0-auto"); }); @@ -80,7 +83,7 @@ describe("Date validity", () => { it("Works with a new date right now", async () => { const serialised = serialiseToMiniSEEDUint8Array([], { sourceIdentifier: "https://zade.viggers.net/example", - startTime: new Date(), + startTime: startTimeFromDate(new Date()), }); await checkData(serialised, "date-utc"); });