@rethinkhealth/hl7v2-util-timestamp
v0.12.0
Published
HL7v2 timestamp parsing, formatting, and conversion with precision tracking
Maintainers
Readme
@rethinkhealth/hl7v2-util-timestamp
An immutable, high-performance HL7v2 timestamp utility with precision tracking. Parses, formats, and converts HL7v2 TS (Time Stamp) values while preserving precision for lossless round-tripping.
Features
- Precision tracking - 7 levels from year to millisecond, never loses or adds detail
- Lossless round-trip -
Timestamp.parse(str).toString() === strfor all valid inputs - High performance - No regex, no intermediate arrays, pre-computed lookup tables
- Type safe - Full TypeScript support with const enums and union types
- Zero dependencies - Pure computation, no runtime dependencies
Installation
npm install @rethinkhealth/hl7v2-util-timestampQuick Start
import { Timestamp, Precision } from "@rethinkhealth/hl7v2-util-timestamp";
// Parse an HL7v2 timestamp string
const ts = Timestamp.parse("20260307143045-0500");
ts.precision; // "second"
ts.toDate(); // Date representing 2026-03-07T19:30:45.000Z
ts.toString(); // "20260307143045-0500"
// Create from a JavaScript Date
const ts2 = Timestamp.from(new Date(), { precision: "day" });
ts2.toString(); // "20260307"
// Current time
const now = Timestamp.now({ precision: "second", timezone: true });
now.toString(); // "20260307143045+0000"API Reference
Timestamp.parse(value: string): Timestamp
Parses an HL7v2 timestamp string into a Timestamp instance.
Accepts the full HL7v2 TS format: YYYY[MM[DD[HH[MM[SS[.S[S[S[S]]]]]]]]][+/-ZZZZ]
Timestamp.parse("2026"); // year precision
Timestamp.parse("202603"); // month precision
Timestamp.parse("20260307"); // day precision
Timestamp.parse("2026030714"); // hour precision
Timestamp.parse("202603071430"); // minute precision
Timestamp.parse("20260307143045"); // second precision
Timestamp.parse("20260307143045.1234"); // millisecond precision (4-digit fractional)
Timestamp.parse("20260307143045-0500"); // with timezone offset
Timestamp.parse("20260307143045.12+0000"); // fractional + timezoneThrows: TypeError if the string is not a valid HL7v2 timestamp.
Timestamp.from(date: Date, options?: TimestampOptions): Timestamp
Creates a Timestamp from a JavaScript Date object.
const date = new Date(2026, 2, 7, 14, 30, 45, 123);
Timestamp.from(date).toString(); // "20260307143045"
Timestamp.from(date, { precision: "day" }).toString(); // "20260307"
Timestamp.from(date, { precision: "millisecond" }).toString(); // "20260307143045.123"
Timestamp.from(date, { timezone: true }).toString(); // "20260307143045+0000"Throws: TypeError if the Date is invalid (NaN).
Timestamp.now(options?: TimestampOptions): Timestamp
Creates a Timestamp for the current time.
Timestamp.now().toString(); // "20260307143045"
Timestamp.now({ precision: "day" }).toString(); // "20260307"
Timestamp.now({ precision: "minute" }).toString(); // "202603071430"
Timestamp.now({ timezone: true }).toString(); // "20260307143045+0000"Instance Properties
precision: Precision
The precision level of the timestamp. One of: "year", "month", "day", "hour", "minute", "second", "millisecond".
Instance Methods
toString(): string
Formats the timestamp as an HL7v2 string, respecting the precision level.
Timestamp.parse("20260307").toString(); // "20260307" (not "20260307000000")toDate(): Date
Returns a copy of the underlying Date object. Each call returns a new copy to preserve immutability.
TimestampOptions
interface TimestampOptions {
/** Precision level for formatting. Default: "second" */
precision?: Precision;
/** Include local timezone offset. Only applies at "hour" precision or finer. Default: false */
timezone?: boolean;
}Precision
import { Precision } from "@rethinkhealth/hl7v2-util-timestamp";
Precision.Year; // "year" → YYYY
Precision.Month; // "month" → YYYYMM
Precision.Day; // "day" → YYYYMMDD
Precision.Hour; // "hour" → YYYYMMDDHH
Precision.Minute; // "minute" → YYYYMMDDHHmm
Precision.Second; // "second" → YYYYMMDDHHmmss
Precision.Millisecond; // "millisecond" → YYYYMMDDHHmmss.SSSSPrecision Tracking
Precision is a first-class concept. The timestamp remembers exactly how much detail was provided, so formatting never inflates or truncates:
// Parsing preserves precision
Timestamp.parse("20260307").toString(); // "20260307" — not "20260307000000"
Timestamp.parse("202603071430").toString(); // "202603071430" — not "20260307143000"
// Fractional seconds are preserved exactly (even 4-digit)
Timestamp.parse("20260307143045.1234").toString(); // "20260307143045.1234"
// from() uses the requested precision
Timestamp.from(date, { precision: "day" }).toString(); // "20260307"
Timestamp.from(date, { precision: "minute" }).toString(); // "202603071430"Timezone Management
Timezone information is embedded directly in the Date object — not stored as separate public metadata. This ensures that toDate() always returns the correct absolute moment, and toString() always reproduces the original timezone for lossless round-tripping.
Correct Absolute Moments
When an HL7v2 timestamp includes a timezone offset (+/-ZZZZ), the offset is applied during parsing to construct a Date with the correct UTC instant:
// 14:30:45 in UTC-5 → Date stores 19:30:45 UTC
const ts = Timestamp.parse("20260307143045-0500");
ts.toDate().toISOString(); // "2026-03-07T19:30:45.000Z"
// 14:30:45 in UTC+5:30 → Date stores 09:00:45 UTC
const ts2 = Timestamp.parse("20260307143045+0530");
ts2.toDate().toISOString(); // "2026-03-07T09:00:45.000Z"
// Midnight in UTC+5:30 → previous day in UTC
const ts3 = Timestamp.parse("20260101000000+0530");
ts3.toDate().toISOString(); // "2025-12-31T18:30:00.000Z"Lossless Round-Trip with Timezone
Parsed timestamps with timezone offsets round-trip exactly, regardless of the server's timezone:
// On ANY server (UTC, EST, PST, IST):
Timestamp.parse("20260307143045-0500").toString(); // "20260307143045-0500"
Timestamp.parse("20260307143045+0530").toString(); // "20260307143045+0530"
Timestamp.parse("20261231235959.999-0800").toString(); // "20261231235959.999-0800"The timezone offset is stored internally and used by toString() to reconstruct the original local time components from UTC. No public timezoneOffset property is exposed — this prevents the data integrity risk of consumers calling toDate() and forgetting to account for a separate offset.
Creating Timestamps with Timezone
When using from() or now() with { timezone: true }, the runtime's local timezone offset is captured at creation time:
const ts = Timestamp.from(new Date(), { timezone: true });
ts.toString(); // "20260307143045-0500" (on a UTC-5 server)Without Timezone
Timestamps without a timezone offset are treated as local time — no UTC conversion is applied:
const ts = Timestamp.parse("20260307143045");
// Date is constructed with new Date(2026, 2, 7, 14, 30, 45) — local timePrecision Gate
Timezone is only included at "hour" precision or finer. Coarser precisions ("year", "month", "day") never carry a timezone suffix:
Timestamp.from(date, { precision: "day", timezone: true }).toString();
// "20260307" — no timezone (day precision is too coarse)
Timestamp.from(date, { precision: "hour", timezone: true }).toString();
// "2026030714-0500" — timezone includedPerformance
Optimized for high-throughput streaming processors handling 100K+ messages per second:
- No regex — manual character-based parsing via
codePointAtarithmetic - No intermediate arrays — direct string concatenation in
toString() - Pre-computed lookup table — zero-padded strings for 0-99
- O(1) precision lookups —
Mapinstead ofArray.indexOf() - Zero allocations in hot paths — no substring extraction or
Number()conversions
Error Handling
All invalid input throws TypeError with a descriptive message:
Timestamp.parse(""); // TypeError: Invalid HL7v2 timestamp: ""
Timestamp.parse("not-a-timestamp"); // TypeError: Invalid HL7v2 timestamp: "not-a-timestamp"
Timestamp.parse("20263"); // TypeError: Invalid HL7v2 timestamp: "20263"
Timestamp.from(new Date("invalid")); // TypeError: Invalid Date provided to Timestamp.fromContributing
We welcome contributions! Please see our Contributing Guide for more details.
- Fork the repository
- Create your feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'Add some amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
Code of Conduct
To ensure a welcoming and positive environment, we have a Code of Conduct that all contributors and participants are expected to adhere to.
License
Copyright 2025 Rethink Health, SUARL. All rights reserved.
This program is licensed to you under the terms of the MIT License. This program is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the LICENSE file for details.
