Tijdstempels met hoge precisie sorteren met Temporal.Instant

Webfuncties: Temporal

Logboekviewers, controletijdlijnen en telemetrie-UI's nemen vaak gebeurtenissen op waarvan de ISO 8601-tijdstempels met microseconde- of nanosecondeprecisie zijn vastgelegd door een server, database of systeemeigen klok (bijvoorbeeld 2026-04-01T12:00:00.000123456Z). Omdat het verouderde Date-object alleen milliseconden opslaat, worden cijfers van minder dan een milliseconde stilzwijgend afgekapt als die tekenreeksen worden geparseerd met new Date() of Date.parse(). Hierdoor botsen afzonderlijke gebeurtenissen binnen dezelfde milliseconde en worden ze in een onstabiele volgorde gesorteerd.

Temporal.Instant vertegenwoordigt een exact punt op de UTC-tijdlijn met een precisie van nanoseconden en zonder gekoppelde tijdzone. Als je vooraf opgenomen tijdstempels met hoge precisie parseert met Temporal.Instant.from(), blijft de volledige nanosecondewaarde behouden, wordt er deterministisch gesorteerd met Temporal.Instant.compare() en worden exacte Temporal.Duration-intervallen tussen gebeurtenissen berekend. In browsers wordt Temporal.Now.instant() opzettelijk beperkt tot milliseconden of microseconden om Spectre-timingaanvallen te beperken. Gebruik Temporal.Instant.from() om tijdstempels in nanoseconden te behouden die door externe klokken worden geproduceerd en gebruik performance.now() als je intervallen van minder dan een milliseconde lokaal in de client meet.

Implementeren

Zo parseert, sorteert en meet u intervallen tussen tijdstempels met hoge precisie:

  1. Geregistreerde tijdstempels parseren als Temporal.Instant: Gebruik Temporal.Instant.from(isoString) voor de tijdstempelreeks die wordt geleverd door de server, database of logregel. Behoud het tijdstempel dat de bron heeft geregistreerd. Vervang het niet door Temporal.Now.instant() in de browser.
  2. Gebeurtenissen chronologisch sorteren: Gebruik Temporal.Instant.compare(a, b) als de vergelijker die wordt doorgegeven aan Array.prototype.sort() of Array.prototype.toSorted(). De volledige nanosecondewaarde wordt vergeleken, zodat de volgorde van submilliseconden behouden blijft.
  3. Intervallen berekenen: Gebruik instant.since(previousInstant) om een Temporal.Duration tussen opeenvolgende gebeurtenissen te krijgen. Gebruik a.equals(b) om gebeurtenissen met identieke tijdstempels te detecteren.
  4. Intervallen weergeven met toLocaleString(): Maak de Temporal.Duration op voor gebruikers met duration.toLocaleString(), die delegeert naar Intl.DurationFormat. Converteer niet naar één Number van nanoseconden voor weergave.
  5. Serialiseren voor overdracht: Gebruik Temporal.Instant.prototype.toString() om een standaard ISO 8601-tekenreeks te genereren (altijd in UTC met een achtervoegsel Z).

Voorbeeldcode: Tijdstempels met hoge precisie sorteren

// 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));

Strategische implementatie en best practices

  • Gebruik WEL Temporal.Instant.from() om tijdstempels met hoge precisie te parseren die zijn geregistreerd op basis van één klok (zoals een server, database of logboekpipeline) die moeten worden geordend of gedifferentieerd. In tegenstelling tot Temporal.PlainDateTime vertegenwoordigt Temporal.Instant een ondubbelzinnig punt op de universele tijdlijn.
  • WEL het tijdstempel behouden dat door de bron is geregistreerd. In browsers wordt Temporal.Now.instant() vastgezet op milliseconde-/microsecondeprecisie voor Spectre-timingaanvallen en mitigatie van fingerprinting. Gebruik performance.now() / performance.mark() voor lokale clientintervallen van minder dan een milliseconde en Temporal.Instant.from() om nanoseconde-tijdstempels van server- of native klokken te parseren.
  • Gebruik WEL Temporal.Instant.compare() voor sorteren en a.equals(b) voor gelijkheidscontroles. Controleer Temporal.Instant.compare(a, b) === 0 niet en vergelijk de uitvoer van .toString() niet.
  • Parseer GEEN tekenreeksen met tijdstempels met hoge precisie met new Date() of Date.parse() voor ordening, omdat ze worden afgekapt tot milliseconden en botsingen veroorzaken.
  • Maak van een interval GEEN enkel getal voor weergave. duration.total('nanoseconds') retourneert een Number, die hoger is dan Number.MAX_SAFE_INTEGER voor perioden langer dan ongeveer 104 dagen en ongemerkt precisie verliest. epochNanoseconds (een BigInt) aftrekken is exact, maar verwijdert de eenheidsstructuur. Houd intervallen als Temporal.Duration en maak ze op met .toLocaleString().
  • WEL: Converteer een Instant naar Temporal.ZonedDateTime met instant.toZonedDateTimeISO(timeZone) als je de gelokaliseerde kloktijd van een gebeurtenis aan een gebruiker wilt tonen.

Reservestrategie

Fallbacks en browserondersteuning voor Temporal

Voor browsers die de native Temporal API nog niet ondersteunen, gebruik je functiedetectie en laad je temporal-polyfill dynamisch (~15 KB min+gz).

Houd er rekening mee dat als je temporal-polyfill importeert, het Temporal-object niet automatisch wordt toegewezen aan het globale bereik. Wijs de waarde expliciet toe als je code afhankelijk is van 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();
})();