Log viewers, audit timelines, and telemetry UIs often ingest events whose ISO 8601 timestamps were recorded with microsecond or nanosecond precision by a server, database, or native clock (for example, 2026-04-01T12:00:00.000123456Z). Because the legacy Date object only stores milliseconds, parsing those strings with new Date() or Date.parse() silently truncates sub-millisecond digits, causing distinct events within the same millisecond to collide and sort in an unstable order.
Temporal.Instant represents an exact point on the UTC timeline with nanosecond precision and no attached time zone. When parsing pre-recorded high-precision timestamps with Temporal.Instant.from(), it preserves the full nanosecond value, sorts deterministically with Temporal.Instant.compare(), and computes exact Temporal.Duration intervals between events. Note that in browsers, Temporal.Now.instant() is deliberately clamped to millisecond or microsecond resolution for Spectre timing-attack mitigation—use Temporal.Instant.from() to preserve nanosecond timestamps produced by external clocks, and use performance.now() when measuring sub-millisecond intervals locally in the client.
How to Implement
To parse, sort, and measure intervals between high-precision timestamps:
- Parse recorded timestamps as
Temporal.Instant: UseTemporal.Instant.from(isoString)on the timestamp string supplied by the server, database, or log line. Keep the timestamp the source recorded; do not replace it withTemporal.Now.instant()in the browser. - Sort events chronologically: Use
Temporal.Instant.compare(a, b)as the comparator passed toArray.prototype.sort()orArray.prototype.toSorted(). It compares the full nanosecond value so sub-millisecond ordering is preserved. - Calculate intervals: Use
instant.since(previousInstant)to get aTemporal.Durationbetween consecutive events. Usea.equals(b)to detect events with identical timestamps. - Display intervals with
toLocaleString(): Format theTemporal.Durationfor users withduration.toLocaleString(), which delegates toIntl.DurationFormat. Do not convert to a singleNumberof nanoseconds for display. - Serialize for transmission: Use
Temporal.Instant.prototype.toString()to emit a standard ISO 8601 string (always in UTC with aZsuffix).
Example Code: Sorting High-Precision Timestamps
// Events received with nanosecond-precision ISO 8601 timestamps.
const rawEvents = [
{ nodeId: 'api-2', eventType: 'db.query', ts: '2026-04-01T12:00:00.000123456Z' },
{ nodeId: 'api-1', eventType: 'request.start', ts: '2026-04-01T12:00:00.000120000Z' },
{ nodeId: 'api-2', eventType: 'request.end', ts: '2026-04-01T12:00:00.000987654Z' },
];
// 1. Parse recorded timestamps as Temporal.Instant (never `new Date(ts)`,
// which drops everything below the millisecond).
const events = rawEvents.map(evt => ({
...evt,
timestamp: Temporal.Instant.from(evt.ts),
}));
// 2. Sort chronologically with the static compare() method
function sequenceEvents(items) {
// Instant.compare returns -1, 0, or 1, which is what sort() expects.
return [...items].sort((a, b) => Temporal.Instant.compare(a.timestamp, b.timestamp));
}
// 3. Calculate and display the interval between consecutive events
function analyzeTrace(sortedEvents) {
for (let i = 1; i < sortedEvents.length; i++) {
const prev = sortedEvents[i - 1];
const curr = sortedEvents[i];
// since() returns a Temporal.Duration that retains nanosecond precision.
const gap = curr.timestamp.since(prev.timestamp);
// 4. toLocaleString() formats the duration for people (e.g., "3μs 456ns").
// Avoid gap.total('nanoseconds'): spans over ~104 days exceed Number.MAX_SAFE_INTEGER.
// 'en-US' is used here for deterministic output; pass undefined to use the user's locale.
console.log(`${prev.eventType} → ${curr.eventType}: ${gap.toLocaleString('en-US', { style: 'narrow' })}`);
}
}
analyzeTrace(sequenceEvents(events));
Strategic Implementation & Best Practices
- DO use
Temporal.Instant.from()for parsing high-precision timestamps recorded against a single clock (such as a server, database, or log pipeline) that need to be ordered or differenced. UnlikeTemporal.PlainDateTime,Temporal.Instantrepresents an unambiguous point on the universal timeline. - DO keep the timestamp recorded by the source. In browsers,
Temporal.Now.instant()is clamped to millisecond/microsecond precision for Spectre timing-attack and fingerprinting mitigation; useperformance.now()/performance.mark()for local sub-millisecond client intervals, andTemporal.Instant.from()for parsing nanosecond timestamps from server or native clocks. - DO use
Temporal.Instant.compare()for sorting anda.equals(b)for equality checks; do not checkTemporal.Instant.compare(a, b) === 0or compare.toString()output. - DO NOT parse high-precision timestamp strings with
new Date()orDate.parse()for ordering, because they truncate to milliseconds and produce collisions. - DO NOT collapse an interval to a single number for display.
duration.total('nanoseconds')returns aNumber, which exceedsNumber.MAX_SAFE_INTEGERfor spans longer than ~104 days and silently loses precision. SubtractingepochNanoseconds(aBigInt) is exact but discards unit structure; keep intervals asTemporal.Durationand format them with.toLocaleString(). - DO convert an
InstanttoTemporal.ZonedDateTimewithinstant.toZonedDateTimeISO(timeZone)when you need to display the localized wall-clock time of an event to a user.
Fallback Strategy
Fallbacks & browser support for Temporal
For browsers that do not yet support the native Temporal API, use feature detection and dynamically load temporal-polyfill (~15 KB min+gz).
Note that importing temporal-polyfill does not automatically assign the Temporal object to the global scope. Assign it explicitly if your code relies on globalThis.Temporal.
// Check if Temporal is supported natively
(async () => {
if (typeof Temporal === 'undefined') {
// Load the polyfill conditionally
const module = await import('https://esm.sh/temporal-polyfill');
globalThis.Temporal = module.Temporal;
// Extend Date.prototype if needed
Date.prototype.toTemporalInstant = module.toTemporalInstant;
}
initializeApp();
})();