Les visionneuses de journaux, les chronologies d'audit et les interfaces utilisateur de télémétrie ingèrent souvent des événements dont les codes temporels ISO 8601 ont été enregistrés avec une précision de l'ordre de la microseconde ou de la nanoseconde par un serveur, une base de données ou une horloge native (par exemple, 2026-04-01T12:00:00.000123456Z). Étant donné que l'ancien objet Date ne stocke que les millisecondes, l'analyse de ces chaînes avec new Date() ou Date.parse() tronque silencieusement les chiffres inférieurs à la milliseconde, ce qui entraîne la collision d'événements distincts au cours de la même milliseconde et leur tri dans un ordre instable.
Temporal.Instant représente un point exact sur la chronologie UTC avec une précision à la nanoseconde et sans fuseau horaire associé. Lors de l'analyse de codes temporels haute précision préenregistrés avec Temporal.Instant.from(), il conserve la valeur complète en nanosecondes, trie de manière déterministe avec Temporal.Instant.compare() et calcule les intervalles Temporal.Duration exacts entre les événements. Notez que dans les navigateurs, Temporal.Now.instant() est délibérément limité à une résolution en millisecondes ou en microsecondes pour atténuer les attaques temporelles Spectre. Utilisez Temporal.Instant.from() pour conserver les codes temporels en nanosecondes produits par des horloges externes, et utilisez performance.now() pour mesurer localement des intervalles inférieurs à la milliseconde dans le client.
Implémentation
Pour analyser, trier et mesurer les intervalles entre des codes temporels de haute précision :
- Analyser les codes temporels enregistrés en tant que
Temporal.Instant: utilisezTemporal.Instant.from(isoString)sur la chaîne de code temporel fournie par le serveur, la base de données ou la ligne de journal. Conservez le code temporel enregistré par la source. Ne le remplacez pas parTemporal.Now.instant()dans le navigateur. - Trier les événements par ordre chronologique : utilisez
Temporal.Instant.compare(a, b)comme comparateur transmis àArray.prototype.sort()ouArray.prototype.toSorted(). Il compare la valeur complète en nanosecondes afin de préserver l'ordre des sous-millisecondes. - Calculer des intervalles : utilisez
instant.since(previousInstant)pour obtenir unTemporal.Durationentre des événements consécutifs. Utiliseza.equals(b)pour détecter les événements ayant des codes temporels identiques. - Afficher les intervalles avec
toLocaleString(): formatez l'Temporal.Durationpour les utilisateurs avecduration.toLocaleString(), qui délègue àIntl.DurationFormat. Ne convertissez pas en un seulNumberde nanosecondes pour l'affichage. - Sérialiser pour la transmission : utilisez
Temporal.Instant.prototype.toString()pour émettre une chaîne ISO 8601 standard (toujours au format UTC avec un suffixeZ).
Exemple de code : Trier des codes temporels de haute précision
// 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));
Implémentation stratégique et bonnes pratiques
- À FAIRE : Utilisez
Temporal.Instant.from()pour analyser les codes temporels de haute précision enregistrés sur une seule horloge (comme un serveur, une base de données ou un pipeline de journaux) qui doivent être ordonnés ou différenciés. Contrairement àTemporal.PlainDateTime,Temporal.Instantreprésente un point sans ambiguïté sur la chronologie universelle. - À FAIRE : Conservez le code temporel enregistré par la source. Dans les navigateurs,
Temporal.Now.instant()est limité à une précision de l'ordre de la milliseconde/microseconde pour atténuer les attaques par canal auxiliaire Spectre et l'empreinte numérique. Utilisezperformance.now()/performance.mark()pour les intervalles client locaux inférieurs à la milliseconde, etTemporal.Instant.from()pour analyser les codes temporels en nanosecondes à partir du serveur ou des horloges natives. - À FAIRE : Utilisez
Temporal.Instant.compare()pour le tri eta.equals(b)pour les vérifications d'égalité. Ne vérifiez pasTemporal.Instant.compare(a, b) === 0et ne comparez pas la sortie.toString(). - NE PAS analyser les chaînes de code temporel de haute précision avec
new Date()ouDate.parse()pour le tri, car elles sont tronquées en millisecondes et produisent des collisions. - NE RÉDUISEZ PAS un intervalle à un seul nombre pour l'affichage.
duration.total('nanoseconds')renvoie unNumber, qui dépasseNumber.MAX_SAFE_INTEGERpour les périodes de plus de 104 jours environ et perd silencieusement en précision. La soustraction deepochNanoseconds(unBigInt) est exacte, mais supprime la structure des unités. Conservez les intervalles sous la formeTemporal.Durationet formatez-les avec.toLocaleString(). - À FAIRE : Convertissez un
InstantenTemporal.ZonedDateTimeavecinstant.toZonedDateTimeISO(timeZone)lorsque vous devez afficher l'heure locale d'un événement pour un utilisateur.
Stratégie de remplacement
Solutions de remplacement et compatibilité des navigateurs avec Temporal
Pour les navigateurs qui ne sont pas encore compatibles avec l'API Temporal native, utilisez la détection de fonctionnalités et chargez dynamiquement temporal-polyfill (~15 Ko min+gz).
Notez que l'importation de temporal-polyfill n'attribue pas automatiquement l'objet Temporal au champ d'application global. Attribuez-la explicitement si votre code repose sur 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();
})();