Als je de verstreken of resterende tijd tussen gebeurtenissen (zoals het verlopen van een proefperiode, het aftellen van een abonnement of SLA-vensters) wilt berekenen en die perioden wilt presenteren in een gelokaliseerde, door mensen leesbare indeling, moest je in het verleden handmatig rekenen of externe bibliotheken gebruiken. Het verouderde Date-object houdt alleen milliseconden bij, waardoor berekeningen met kalendermaanden, overgangen naar zomertijd en eenheidsbalancering foutgevoelig zijn.
De Temporal API koppelt Temporal.ZonedDateTime aan Temporal.Duration en Intl.DurationFormat om DST-veilige tijdsverschillen, instelbare eenheidsbalans, kalenderbewuste duurvergelijkingen en gelokaliseerde opmaak te bieden.
Implementeren
- Eindpunten van
Temporal.ZonedDateTimeverkrijgen: GebruikTemporal.Now.zonedDateTimeISO()voor de huidige tijd in de tijdzone van het systeem of geef een expliciete IANA-tijdzonetekenreeks door (Temporal.Now.zonedDateTimeISO(timeZone)) als de tijdzone van de doelgebeurtenis verschilt van de systeemstandaard van de gebruiker. - Verschillen berekenen met
.since()en.until():- Gebruik
now.since(start, options)om de verstreken tijd sinds een eerdere gebeurtenis te berekenen. - Gebruik
now.until(end, options)om de resterende tijd tot een toekomstige gebeurtenis te berekenen. - Geef
largestUnit(bijv.'year','month','day','hour') ensmallestUnit(bijv.'minute') door om de resulterendeTemporal.Durationin evenwicht te brengen en af te ronden.
- Gebruik
- Zelfstandige
Temporal.Duration-objecten in evenwicht brengen of vergelijken:- Maak zelfstandige duurwaarden met
Temporal.Duration.from({ minutes: 90 })en breng eenheden opnieuw in balans metduration.round({ largestUnit: 'hour' })(geefrelativeToop als je de balans opmaakt voor kalendermaanden, jaren of overgangen naar zomertijd). - Vergelijk 2 perioden met
Temporal.Duration.compare(d1, d2, { relativeTo }). Je moet eenPlainDateofZonedDateTimedoorgeven alsrelativeToals een van de perioden kalendereenheden (years,months,weeks) bevat of de grenzen van de zomertijd overschrijdt.
- Maak zelfstandige duurwaarden met
- Indeling voor mensen versus machines:
- Gebruik
duration.toLocaleString(locale, options)(die delegeert naarIntl.DurationFormat) voor gelokaliseerde, op gebruikers gerichte tekst. - Gebruik
duration.toString()alleen voor machinaal leesbare ISO 8601-duurtekenreeksen (zoalsP1M2DT4H).
- Gebruik
Voorbeeld: verschil in vervaldatum van proefperiode en gelokaliseerde opmaak
// 1. Get the current time in an explicit target IANA time zone (or omit the
// argument to use the system's current time zone).
const tz = 'America/New_York';
const now = Temporal.Now.zonedDateTimeISO(tz);
// 2. Combine separate date and time strings directly.
// Do not build an ISO string with template literals; chain Temporal methods instead.
const startDateStr = '2025-01-01';
const startTimeStr = '12:00:00';
const endDateStr = '2025-01-31';
const endTimeStr = '12:00:00';
const start = Temporal.PlainDate.from(startDateStr).toPlainDateTime(startTimeStr).toZonedDateTime(tz);
const end = Temporal.PlainDate.from(endDateStr).toPlainDateTime(endTimeStr).toZonedDateTime(tz);
// 3. Calculate differences using .since() and .until()
// ZonedDateTime differences default to largestUnit: 'hour' (days are not a fixed
// length across DST, so Temporal won't balance into days unless asked). Pass
// largestUnit: 'day' or a calendar unit ('week', 'month', 'year') when you want a
// human-scale breakdown instead of e.g. PT720H. smallestUnit rounds away
// seconds and sub-second noise so the formatted string stays readable.
const timeActive = now.since(start, { largestUnit: 'year', smallestUnit: 'minute' });
const timeRemaining = now.until(end, { largestUnit: 'year', smallestUnit: 'minute' });
// 4. Format durations for display with .toLocaleString() (Intl.DurationFormat)
// instead of manually concatenating unit numbers and suffixes. 'en-US' is used
// here for deterministic output; pass undefined to use the user's locale.
console.log(`Active: ${timeActive.toLocaleString('en-US', { style: 'narrow' })}`);
console.log(`Remaining: ${timeRemaining.toLocaleString('en-US', { style: 'long' })}`);
// Use .toString() for ISO 8601 serialization (e.g., API payloads or <time datetime="...">)
console.log(`ISO 8601: ${timeRemaining.toString()}`);
// 5. Check status via .sign, and compare durations with Temporal.Duration.compare()
const isExpired = timeRemaining.sign < 0;
// Pass relativeTo when comparing durations that may include calendar months/years or DST shifts
const isPastHalfway = Temporal.Duration.compare(timeActive, timeRemaining, { relativeTo: now }) > 0;
Voorbeeld: Zelfstandige duur in evenwicht brengen (round en Intl.DurationFormat)
// Convert a raw unit count (e.g., 90 minutes) into a balanced breakdown (1 hour, 30 minutes)
const rawDuration = Temporal.Duration.from({ minutes: 90 });
const balanced = rawDuration.round({ largestUnit: 'hour' });
// Format via duration.toLocaleString() or directly with Intl.DurationFormat
const formatter = new Intl.DurationFormat('en-US', { style: 'long' });
console.log(formatter.format(balanced)); // "1 hour, 30 minutes"
Strategische implementatie en best practices
- Gebruik WEL
Temporal.ZonedDateTimevoor berekeningen met gebeurtenissen in de echte wereld die plaatsvinden in specifieke tijdzones (zoals abonnementsverlengingen of aftellen naar evenementen) en geef een explicietetimeZonedoor aanTemporal.Now.zonedDateTimeISO(timeZone)als je een berekening uitvoert voor een andere zone dan de systeemstandaard. - WEL: Combineer afzonderlijke datum- en tijdtekenreeksen met Temporal-methoden, zoals
Temporal.PlainDate.from(dateStr).toPlainDateTime(timeStr).toZonedDateTime(tz)ofTemporal.PlainDate.from(dateStr).toZonedDateTime({ timeZone: tz, plainTime: timeStr }), in plaats van een ISO-tekenreeks zoals`${dateStr}T${timeStr}`te interpoleren. - Geef
largestUnit(bijv.'day','month','year') door in.since(),.until()of.round()als je de duur wilt balanceren buiten uren. De standaardwaarde voorZonedDateTime-verschilmethoden is'hour'. - Gebruik WEL
Temporal.Duration.compare(d1, d2, { relativeTo })als je 2 perioden vergelijkt en geef eenrelativeTo-anker op als een van de perioden kalendereenheden (months,years) of tijdzonebewuste dagen bevat. - WEL: Maak de duur op voor weergave aan de gebruiker met
duration.toLocaleString()ofnew Intl.DurationFormat(locale, { style })('long','short','narrow'of'digital') in plaats van${duration.years}y ${duration.months}m...handmatig te interpoleren. - Gebruik WEL
duration.toString()voor machinaal leesbare ISO 8601-duurtekenreeksen (bijv.PT1H30M), nooit als op gebruikers gerichte tekst. - WEL: Render een
PlainTimealsHH:MM(bijvoorbeeld voor<input type="time">) met.toPlainTime().toString({ smallestUnit: 'minute' })in plaats van de tekenreeks.slice(0, 5). - Wijzig instanties NIET rechtstreeks.
Temporal-objecten zijn onveranderlijk. Bewerkingen zoals.add(),.subtract(),.round()of.with()retourneren een nieuwe instantie. - Gebruik GEEN
Date/.getTime()-rekenkunde voor het verschil zelf.Dateheeft geen tijdzonebewuste agendawiskunde en alleen millisecondeprecisie. - Gebruik WEL de native property
.signvan de berekendeTemporal.Duration(ofTemporal.ZonedDateTime.compare()) om te controleren of een duur een tijdstip in het verleden/verlopen tijdstip (sign < 0) of een toekomstige start in behandeling vertegenwoordigt.
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();
})();