Dyk in i JavaScript Temporal Duration, det moderna API:et för precis tidsintervallaritmetik, jÀmförelse och formatering. LÀr dig hantera tidsperioder pÄ ett globalt medvetet sÀtt och undvik vanliga fallgropar med Date-objektet.
JavaScript Temporal Duration: BemÀstra tidsintervallaritmetik och formatering för globala applikationer
Att hantera tid inom mjukvaruutveckling Àr notoriskt komplext. FrÄn att spÄra projekttidslinjer över kontinenter till att schemalÀgga internationella videokonferenser kan nyanserna av tidsintervall, tidszoner och sommartid snabbt leda till subtila men kritiska buggar. I Ärtionden har JavaScript-utvecklare brottats med det inbyggda Date-objektet, ett verktyg som, Àven om det Àr funktionellt för enkla tidpunkter, inte rÀcker till nÀr det gÀller precis tidsintervallaritmetik och robust, globalt medveten tidshantering.
HĂ€r kommer JavaScript Temporal API â ett banbrytande förslag designat för att erbjuda ett modernt, robust och anvĂ€ndarvĂ€nligt API för att arbeta med datum och tider i JavaScript. Bland dess kraftfulla nya typer framstĂ„r Temporal.Duration som den definitiva lösningen för att hantera tidsintervall. Denna artikel kommer att ta dig pĂ„ en djupdykning i Temporal.Duration, och utforska dess förmĂ„gor för aritmetik, jĂ€mförelse och intelligent formatering, för att sĂ€kerstĂ€lla att dina applikationer kan hantera tid med global precision och tydlighet.
Oavsett om du bygger ett globalt logistiksystem, en finansiell handelsplattform eller en evenemangsplanerare för flera tidszoner, Àr förstÄelsen för Temporal.Duration avgörande för att eliminera tidsrelaterade tvetydigheter och leverera pÄlitliga, internationaliserade anvÀndarupplevelser.
Brister hos JavaScripts Date-objekt för tidsintervall
Innan vi firar ankomsten av Temporal.Duration Ă€r det viktigt att förstĂ„ begrĂ€nsningarna med det befintliga Date-objektet, sĂ€rskilt nĂ€r det gĂ€ller tidsintervall. Date-objektet representerar en specifik tidpunkt, mĂ€tt i millisekunder sedan Unix-epoken (1 januari 1970, UTC). Ăven om det kan anvĂ€ndas för att utföra grundlĂ€ggande aritmetik, har det flera inneboende brister som gör det olĂ€mpligt för robust hantering av varaktigheter:
-
Mutabilitet:
Date-objekt Àr muterbara. Varje operation pÄ ettDate-objekt Àndrar dess interna tillstÄnd, vilket kan leda till ovÀntade sidoeffekter och svÄrspÄrade buggar, sÀrskilt i komplexa applikationer eller samtidiga miljöer.const d = new Date('2023-01-15T10:00:00Z'); const d2 = d; // d2 now references the same object as d d.setHours(d.getHours() + 1); console.log(d.toISOString()); // 2023-01-15T11:00:00.000Z console.log(d2.toISOString()); // 2023-01-15T11:00:00.000Z (d2 also changed!) -
Avsaknad av ett varaktighetskoncept:
Date-objektet har inget direkt koncept för en "varaktighet" eller "period". Att berÀkna skillnaden mellan tvÄ datum resulterar i ett antal millisekunder, som sedan manuellt mÄste konverteras till Är, mÄnader, dagar, etc. Denna manuella konvertering Àr felbenÀgen, sÀrskilt nÀr man hanterar mÄnader med varierande lÀngd eller skottÄr.const date1 = new Date('2023-01-01T00:00:00Z'); const date2 = new Date('2023-03-01T00:00:00Z'); const diffMs = date2.getTime() - date1.getTime(); // How many months is this? What about leap years? // (diffMs / (1000 * 60 * 60 * 24 * 30)) is an approximation at best. console.log(`Difference in milliseconds: ${diffMs}`); console.log(`Approximate days: ${diffMs / (1000 * 60 * 60 * 24)}`); // Works for days, but not robust for months/years -
Tidszonstvetydighet:
Date-objekt blandar ofta ihop lokal tid och UTC. Ăven om de internt lagrar UTC-millisekunder, arbetar deras metoder ofta med systemets lokala tidszon som standard, vilket leder till förvirring och inkonsekvenser nĂ€r man arbetar med distribuerade system eller internationella anvĂ€ndare. - Utmaningar med sommartid (DST): ĂvergĂ„ngar till sommartid kan göra att dagar blir 23 eller 25 timmar lĂ„nga. Enkel aritmetik (t.ex. att lĂ€gga till 24 timmar till ett datum) kanske inte alltid resulterar i nĂ€sta kalenderdag, vilket leder till felaktiga berĂ€kningar nĂ€r en "dag" antas vara en fast 24-timmarsperiod.
Dessa begrÀnsningar har historiskt sett tvingat utvecklare att förlita sig pÄ tredjepartsbibliotek som Moment.js eller date-fns, eller att skriva komplex, felbenÀgen anpassad kod för att hantera tidsintervall korrekt. Temporal syftar till att införa dessa funktioner direkt i JavaScript.
Introduktion till JavaScript Temporal: En modern metod för tidshantering
Temporal API Àr ett omfattande, nytt globalt objekt i JavaScript designat för att vara en modern ersÀttning för det Àldre Date-objektet. Dess kÀrnprinciper Àr oförÀnderlighet (immutability), explicit hantering av tidszoner och en tydlig uppdelning av ansvarsomrÄden mellan olika tidskoncept. Temporal introducerar flera nya klasser, var och en representerande en distinkt aspekt av tid:
Temporal.Instant: En specifik, otvetydig tidpunkt, oberoende av nÄgon kalender eller tidszon (liknande en Unix-tidsstÀmpel, men med nanosekundprecision).Temporal.ZonedDateTime: En specifik tidpunkt i en viss kalender och tidszon. Detta Àr den mest kompletta representationen av ett specifikt datum och tid för en anvÀndare.Temporal.PlainDate: Ett kalenderdatum (Är, mÄnad, dag) utan tid eller tidszon.Temporal.PlainTime: En klocktid (timme, minut, sekund, etc.) utan datum eller tidszon.Temporal.PlainDateTime: Ett kalenderdatum och klocktid tillsammans, utan tidszon.Temporal.PlainYearMonth: Ett specifikt Är och mÄnad i ett kalendersystem.Temporal.PlainMonthDay: En specifik mÄnad och dag i ett kalendersystem.Temporal.Duration: En signerad tidslÀngd, sÄsom "5 timmar och 30 minuter" eller "2 dagar". Detta Àr vÄrt fokus i denna guide.
Alla Temporal-objekt Àr oförÀnderliga, vilket innebÀr att operationer som att lÀgga till eller subtrahera tid skapar nya objekt istÀllet för att modifiera befintliga, vilket ökar förutsÀgbarheten och minskar antalet buggar.
FörstÄ Temporal.Duration
En Temporal.Duration representerar en tidslÀngd. Avgörande Àr att den Àr oberoende av en specifik start- eller slutpunkt. Det Àr helt enkelt "hur mycket tid" som har förflutit eller kommer att förflyta. Den kan bestÄ av Är, mÄnader, veckor, dagar, timmar, minuter, sekunder, millisekunder, mikrosekunder och nanosekunder. Varje komponent Àr ett heltal och kan vara positivt eller negativt.
Till exempel Àr "2 timmar och 30 minuter" en varaktighet. "Perioden frÄn 1 januari till 1 mars" Àr en varaktighet mellan tvÄ specifika punkter, som kan *representeras* av en Temporal.Duration, men sjÀlva Duration Àr bara intervallet.
Skapa en varaktighet
Det finns flera enkla sÀtt att skapa Temporal.Duration-objekt:
1. AnvÀnda konstruktorn
Konstruktorn lÄter dig specificera varje komponent direkt. Notera att argumenten Àr ordnade frÄn största enhet (Är) till minsta (nanosekunder).
// new Temporal.Duration(years, months, weeks, days, hours, minutes, seconds, milliseconds, microseconds, nanoseconds)
// A duration of 2 hours and 30 minutes
const duration1 = new Temporal.Duration(0, 0, 0, 0, 2, 30, 0, 0, 0, 0);
console.log(duration1.toString()); // P2H30M
// A duration of 1 year, 2 months, 3 days
const duration2 = new Temporal.Duration(1, 2, 0, 3);
console.log(duration2.toString()); // P1Y2M3D
// A duration of -5 days
const duration3 = new Temporal.Duration(0, 0, 0, -5);
console.log(duration3.toString()); // P-5D
2. AnvÀnda Temporal.Duration.from() med ett objekt
Detta Àr ofta det mest lÀsbara sÀttet att skapa varaktigheter, vilket lÄter dig specificera endast de komponenter du behöver.
// Duration of 1.5 hours
const halfHourDuration = Temporal.Duration.from({ hours: 1, minutes: 30 });
console.log(halfHourDuration.toString()); // P1H30M
// Duration of 7 days (1 week)
const oneWeekDuration = Temporal.Duration.from({ days: 7 });
console.log(oneWeekDuration.toString()); // P7D
// Duration with fractional seconds (e.g., 2.5 seconds)
const twoPointFiveSeconds = Temporal.Duration.from({ seconds: 2, milliseconds: 500 });
console.log(twoPointFiveSeconds.toString()); // PT2.5S
// Negative duration
const negativeDuration = Temporal.Duration.from({ minutes: -45 });
console.log(negativeDuration.toString()); // PT-45M
3. AnvÀnda Temporal.Duration.from() med en ISO 8601-strÀng
Temporal anvÀnder sig av ISO 8601-formatet för varaktigheter, vilket Àr en standard för att representera varaktigheter. Detta Àr utmÀrkt för att tolka varaktigheter frÄn externa datakÀllor.
Formatet ser generellt ut som P[years]Y[months]M[weeks]W[days]DT[hours]H[minutes]M[seconds]S. T separerar datumkomponenter frÄn tidskomponenter.
// 1 year, 2 months, 3 days
const isoDuration1 = Temporal.Duration.from('P1Y2M3D');
console.log(isoDuration1.toString()); // P1Y2M3D
// 4 hours, 5 minutes, 6 seconds
const isoDuration2 = Temporal.Duration.from('PT4H5M6S');
console.log(isoDuration2.toString()); // PT4H5M6S
// A combined duration
const isoDuration3 = Temporal.Duration.from('P7DT12H30M');
console.log(isoDuration3.toString()); // P7DT12H30M
// Fractional seconds are also supported
const isoDuration4 = Temporal.Duration.from('PT1.5S');
console.log(isoDuration4.toString()); // PT1.5S
Utföra aritmetik med varaktigheter
Den sanna kraften hos Temporal.Duration lyser igenom i dess aritmetiska förmÄgor. Du kan addera, subtrahera, multiplicera och dividera varaktigheter, och Àven addera/subtrahera dem frÄn andra Temporal-datum/tidstyper. Alla operationer returnerar nya Temporal.Duration-objekt pÄ grund av oförÀnderligheten.
Addera varaktigheter
add()-metoden kombinerar tvÄ varaktigheter.
const sprintDuration = Temporal.Duration.from({ weeks: 2 });
const bufferDuration = Temporal.Duration.from({ days: 3 });
const totalProjectTime = sprintDuration.add(bufferDuration);
console.log(totalProjectTime.toString()); // P2W3D (or P17D if normalized later)
// Adding a negative duration is equivalent to subtraction
const result = Temporal.Duration.from({ hours: 5 }).add({ hours: -2 });
console.log(result.toString()); // PT3H
Du kan ocksÄ addera en varaktighet till vilket Temporal-datum/tidsobjekt som helst. Det Àr hÀr magin sker, eftersom Temporal korrekt hanterar tidszonsförskjutningar och övergÄngar till sommartid nÀr det Àr relevant.
const projectStart = Temporal.PlainDateTime.from('2023-10-26T09:00:00');
const projectDuration = Temporal.Duration.from({ days: 10, hours: 4 });
const projectEnd = projectStart.add(projectDuration);
console.log(projectEnd.toString()); // 2023-11-05T13:00:00
// With a ZonedDateTime, the time zone rules are applied correctly
const meetingStartUTC = Temporal.ZonedDateTime.from('2024-03-09T14:00:00[UTC]');
const meetingDuration = Temporal.Duration.from({ hours: 1, minutes: 45 });
const meetingEndUTC = meetingStartUTC.add(meetingDuration);
console.log(meetingEndUTC.toString()); // 2024-03-09T15:45:00+00:00[UTC]
// Example crossing a DST boundary (assuming 'Europe/Berlin' shifts at 03:00 on 2024-03-31)
const springForwardStart = Temporal.ZonedDateTime.from('2024-03-30T22:00:00[Europe/Berlin]');
const twentyFourHours = Temporal.Duration.from({ hours: 24 });
const nextDay = springForwardStart.add(twentyFourHours); // Adds 24 actual hours
console.log(springForwardStart.toString()); // 2024-03-30T22:00:00+01:00[Europe/Berlin]
console.log(nextDay.toString()); // 2024-03-31T23:00:00+02:00[Europe/Berlin] (Local time skipped an hour)
Notera hur additionen av 24 timmar till 2024-03-30T22:00:00 i Berlin (som Àr UTC+1) resulterar i 2024-03-31T23:00:00 (nu UTC+2). Klockan hoppade fram en timme, sÄ klocktiden Àr en timme senare pÄ samma datum i förhÄllande till den ursprungliga klocktiden. Detta demonstrerar exakt Temporals medvetenhet om tidszoner och sommartid nÀr man utför aritmetik pÄ `ZonedDateTime`.
Subtrahera varaktigheter
subtract()-metoden fungerar pÄ liknande sÀtt som add(), men den tar bort tid.
const deadlineDuration = Temporal.Duration.from({ days: 30 });
const gracePeriod = Temporal.Duration.from({ days: 5 });
const effectiveDeadline = deadlineDuration.subtract(gracePeriod);
console.log(effectiveDeadline.toString()); // P25D
const taskEnd = Temporal.PlainDateTime.from('2023-12-01T17:00:00');
const taskDuration = Temporal.Duration.from({ hours: 8, minutes: 30 });
const taskStart = taskEnd.subtract(taskDuration);
console.log(taskStart.toString()); // 2023-12-01T08:30:00
Multiplicera och dividera varaktigheter
Metoderna multiply() och divide() skalar komponenterna i en varaktighet med en given faktor. Detta Àr anvÀndbart för scenarier som att berÀkna total tid för flera iterationer av en uppgift.
const trainingSession = Temporal.Duration.from({ minutes: 45 });
const weeklyTraining = trainingSession.multiply(5); // Five sessions a week
console.log(weeklyTraining.toString()); // PT225M
const totalProjectHours = Temporal.Duration.from({ hours: 160 });
const teamMembers = 4;
const hoursPerMember = totalProjectHours.divide(teamMembers);
console.log(hoursPerMember.toString()); // PT40H
Negera varaktigheter
negate()-metoden vÀnder pÄ tecknet för alla komponenter i en varaktighet. En positiv varaktighet blir negativ, och vice versa.
const delayDuration = Temporal.Duration.from({ hours: 3 });
const advanceDuration = delayDuration.negate();
console.log(delayDuration.toString()); // PT3H
console.log(advanceDuration.toString()); // PT-3H
AbsolutvÀrdet av varaktigheter
abs()-metoden returnerar en ny Temporal.Duration med alla komponenter gjorda positiva, vilket effektivt ger dig storleken pÄ varaktigheten oavsett dess tecken.
const negativeDelay = Temporal.Duration.from({ minutes: -60 });
const positiveDuration = negativeDelay.abs();
console.log(negativeDelay.toString()); // PT-60M
console.log(positiveDuration.toString()); // PT60M
JÀmföra och normalisera varaktigheter
Att jÀmföra varaktigheter kan vara knepigt, sÀrskilt nÀr olika enheter Àr inblandade (t.ex. Àr 1 mÄnad lika med 30 dagar?). Temporal tillhandahÄller verktyg för bÄde jÀmförelse och normalisering för att hantera dessa komplexiteter.
JÀmföra varaktigheter med compare()
Den statiska metoden Temporal.Duration.compare(duration1, duration2, options) returnerar:
-1omduration1Àr mindre Ànduration20omduration1Àr lika medduration21omduration1Àr större Ànduration2
Avgörande Àr att nÀr du jÀmför varaktigheter som inkluderar enheter med variabel lÀngd som Är, mÄnader eller veckor, behöver du ofta ange ett relativeTo-alternativ. Denna parameter Àr ett `Temporal.ZonedDateTime`- eller `Temporal.PlainDateTime`-objekt som ger sammanhang för hur dessa enheter ska tolkas (t.ex. hur mÄnga dagar det Àr i en specifik mÄnad eller ett Är).
const oneHour = Temporal.Duration.from({ hours: 1 });
const sixtyMinutes = Temporal.Duration.from({ minutes: 60 });
console.log(Temporal.Duration.compare(oneHour, sixtyMinutes)); // 0 (De Àr ekvivalenta)
const oneMonth = Temporal.Duration.from({ months: 1 });
const thirtyDays = Temporal.Duration.from({ days: 30 });
// Without relativeTo, month/year comparisons are difficult
console.log(Temporal.Duration.compare(oneMonth, thirtyDays)); // 0 (Temporal gör en rimlig gissning utan kontext, ofta baserat pÄ genomsnitt)
// With relativeTo, the comparison is precise based on the context's calendar
const startOfJanuary = Temporal.PlainDate.from('2023-01-01');
const endOfFebruaryLeap = Temporal.PlainDate.from('2024-02-01'); // Leap year
// In January 2023, 1 month is 31 days
const comparisonJan = Temporal.Duration.compare(oneMonth, thirtyDays, { relativeTo: startOfJanuary });
console.log(`1 month vs 30 days in Jan 2023: ${comparisonJan}`); // 1 (1 mÄnad > 30 dagar)
// In February 2024 (leap year), 1 month is 29 days
const comparisonFeb = Temporal.Duration.compare(oneMonth, thirtyDays, { relativeTo: endOfFebruaryLeap });
console.log(`1 month vs 30 days in Feb 2024: ${comparisonFeb}`); // -1 (1 mÄnad < 30 dagar)
Normalisera varaktigheter med normalize() och round()
Varaktigheter kan representeras pÄ mÄnga sÀtt (t.ex. 90 minuter eller 1 timme och 30 minuter). Normalisering och avrundning hjÀlper till att standardisera dessa representationer för konsekvens och visning.
normalize()
normalize()-metoden förenklar varaktighetskomponenter dÀr det Àr möjligt (t.ex. blir 60 minuter 1 timme, 24 timmar blir 1 dag, förutsatt att `relativeTo`-kontexten tillÄter det om mÄnader/Är Àr inblandade).
const longMinutes = Temporal.Duration.from({ minutes: 90 });
console.log(longMinutes.toString()); // PT90M
console.log(longMinutes.normalize().toString()); // PT1H30M
const multipleDays = Temporal.Duration.from({ hours: 48 });
console.log(multipleDays.toString()); // PT48H
console.log(multipleDays.normalize().toString()); // P2D
round()
round()-metoden Àr mer kraftfull för att omvandla och avrunda varaktigheter till specifika enheter. Den tar ett alternativobjekt med:
largestUnit: Den största enheten att inkludera i resultatet (t.ex. 'years', 'days', 'hours').smallestUnit: Den minsta enheten att inkludera i resultatet (t.ex. 'minutes', 'seconds', 'milliseconds').roundingIncrement: Ett heltal att avrunda den minsta enheten med (t.ex. 5 för avrundning till nÀrmaste 5 minuter).roundingMode: Hur man hanterar oavgjorda fall (t.ex. 'halfExpand', 'trunc', 'ceil', 'floor').relativeTo: KrÀvs för att avrunda varaktigheter som innehÄller Är, mÄnader eller veckor.
const complexDuration = Temporal.Duration.from({ hours: 2, minutes: 45, seconds: 30 });
// Round to the nearest hour
const roundedToHours = complexDuration.round({ smallestUnit: 'hour' });
console.log(roundedToHours.toString()); // PT3H
// Round to the nearest 30 minutes, keeping hours
const roundedTo30Minutes = complexDuration.round({
largestUnit: 'hour',
smallestUnit: 'minute',
roundingIncrement: 30
});
console.log(roundedTo30Minutes.toString()); // PT3H
const preciseDuration = Temporal.Duration.from({ minutes: 123, seconds: 45 });
// Display as hours and minutes
const formattedDuration = preciseDuration.round({ largestUnit: 'hour', smallestUnit: 'minute' });
console.log(formattedDuration.toString()); // PT2H4M
// Rounding with months/years requires relativeTo
const longTermDuration = Temporal.Duration.from({ months: 1, days: 10 });
const referenceDate = Temporal.PlainDate.from('2023-01-15');
// Round to months, relative to a date
const roundedToMonths = longTermDuration.round({ largestUnit: 'month', smallestUnit: 'month', relativeTo: referenceDate });
console.log(roundedToMonths.toString()); // P1M
BerÀkna varaktigheter mellan Temporal-objekt
En av de vanligaste anvÀndningarna av varaktigheter Àr att berÀkna tidsintervallet mellan tvÄ specifika tidpunkter. Temporal tillhandahÄller metoderna until() och since() pÄ sina datum/tidsobjekt för detta ÀndamÄl.
until()-metoden
until()-metoden berÀknar varaktigheten frÄn mottagarobjektet till argumentobjektet. Den Àr inkluderande för startpunkten och exkluderande för slutpunkten. Den tar ett alternativobjekt liknande round() för att specificera de önskade enheterna och avrundningsbeteendet.
const startDate = Temporal.PlainDate.from('2023-01-01');
const endDate = Temporal.PlainDate.from('2023-03-15');
// Duration in largest possible units (months, then days)
const projectLength = startDate.until(endDate);
console.log(projectLength.toString()); // P2M14D
// Duration purely in days
const totalDays = startDate.until(endDate, { largestUnit: 'day' });
console.log(totalDays.toString()); // P73D
// Duration between two specific times, respecting time zones
const meetingStart = Temporal.ZonedDateTime.from('2024-07-20T10:00:00[America/New_York]');
const meetingEnd = Temporal.ZonedDateTime.from('2024-07-20T11:30:00[America/New_York]');
const elapsedMeetingTime = meetingStart.until(meetingEnd, { largestUnit: 'hour', smallestUnit: 'minute' });
console.log(elapsedMeetingTime.toString()); // PT1H30M
// Cross-timezone duration (from NYC to London)
const nyStartTime = Temporal.ZonedDateTime.from('2024-08-01T09:00:00[America/New_York]');
const londonEndTime = Temporal.ZonedDateTime.from('2024-08-01T17:00:00[Europe/London]');
const travelDuration = nyStartTime.until(londonEndTime);
console.log(travelDuration.toString()); // PT13H (Actual elapsed time, not wall-clock difference)
Det sista exemplet Ă€r sĂ€rskilt insiktsfullt. Ăven om New York ligger 5 timmar efter London, och klocktiderna Ă€r 9:00 och 17:00 pĂ„ samma dag, berĂ€knar until()-metoden korrekt den faktiska förflutna tiden pĂ„ 13 timmar. Detta beror pĂ„ att ZonedDateTime implicit hanterar tidszonsskillnaden.
since()-metoden
since()-metoden Àr motsatsen till until(). Den berÀknar varaktigheten frÄn argumentobjektet till mottagarobjektet, vilket resulterar i en negativ varaktighet om argumentet ligger i framtiden i förhÄllande till mottagaren.
const currentDateTime = Temporal.ZonedDateTime.from('2024-06-15T12:00:00[Europe/Paris]');
const historicEvent = Temporal.ZonedDateTime.from('2024-01-01T00:00:00[Europe/Paris]');
const timeSinceEvent = currentDateTime.since(historicEvent, { largestUnit: 'month', smallestUnit: 'day' });
console.log(timeSinceEvent.toString()); // P5M14D
const futureDate = Temporal.PlainDate.from('2025-01-01');
const pastDate = Temporal.PlainDate.from('2024-01-01');
const durationFromFuture = pastDate.since(futureDate);
console.log(durationFromFuture.toString()); // P-1Y
Hantera olika enheter och avrundning för berÀknade varaktigheter
NÀr man berÀknar varaktigheter Àr det ofta nödvÀndigt att specificera `largestUnit` och `smallestUnit` för att fÄ en mÀnskligt lÀsbar representation, sÀrskilt för Älder, förfluten tid eller nedrÀkningar.
const birthDate = Temporal.PlainDate.from('1990-07-15');
const today = Temporal.PlainDate.from('2024-06-15');
// Calculate age in years, months, and days
const age = birthDate.until(today, { largestUnit: 'year', smallestUnit: 'day' });
console.log(`Age: ${age.years} years, ${age.months} months, ${age.days} days`); // Age: 33 years, 11 months, 0 days
// Calculate time remaining for a task in hours and minutes
const now = Temporal.Instant.fromEpochSeconds(Date.now() / 1000);
const deadline = Temporal.Instant.from('2024-07-01T09:00:00Z');
const timeLeft = now.until(deadline, { largestUnit: 'hour', smallestUnit: 'minute', roundingMode: 'ceil' });
console.log(`Time left: ${timeLeft.hours} hours and ${timeLeft.minutes} minutes.`); // Example: Time left: 355 hours and 38 minutes.
Formatera varaktigheter för en global publik
Ăven om Temporal.Duration ger exakta, programmatiska representationer av tidsintervall, har den inte en inbyggd toLocaleString()-metod. Detta Ă€r avsiktligt: varaktigheter Ă€r abstrakta tidslĂ€ngder, och deras visning kan variera kraftigt beroende pĂ„ kontext, sprĂ„k och önskad detaljnivĂ„. Du, som utvecklare, Ă€r ansvarig för att presentera varaktigheter pĂ„ ett anvĂ€ndarvĂ€nligt och globalt förstĂ„eligt sĂ€tt.
ISO 8601-strÀngrepresentation
Standardmetoden toString() för ett Temporal.Duration-objekt returnerar dess ISO 8601-strÀngrepresentation. Detta Àr utmÀrkt för kommunikation mellan maskiner, serialisering och lagring, men sÀllan för direkt visning för slutanvÀndare.
const examDuration = Temporal.Duration.from({ hours: 2, minutes: 15 });
console.log(examDuration.toString()); // PT2H15M
const holidayDuration = Temporal.Duration.from({ weeks: 2, days: 3 });
console.log(holidayDuration.toString()); // P2W3D
Manuell formatering för lÀsbarhet och internationalisering
För visning mot anvÀndare kommer du vanligtvis att extrahera komponenterna frÄn en varaktighet och formatera dem med hjÀlp av strÀnginterpolation och JavaScripts Intl API.
HÀr Àr ett exempel pÄ en anpassad funktion som formaterar en varaktighet:
function formatDurationToHumanReadable(duration, locale = 'en-US') {
const parts = [];
// Using Intl.NumberFormat for locale-aware number formatting
const numberFormatter = new Intl.NumberFormat(locale);
if (duration.years !== 0) {
parts.push(numberFormatter.format(duration.years) + ' ' + (duration.years === 1 ? 'year' : 'years'));
}
if (duration.months !== 0) {
parts.push(numberFormatter.format(duration.months) + ' ' + (duration.months === 1 ? 'month' : 'months'));
}
if (duration.weeks !== 0) {
parts.push(numberFormatter.format(duration.weeks) + ' ' + (duration.weeks === 1 ? 'week' : 'weeks'));
}
if (duration.days !== 0) {
parts.push(numberFormatter.format(duration.days) + ' ' + (duration.days === 1 ? 'day' : 'days'));
}
if (duration.hours !== 0) {
parts.push(numberFormatter.format(duration.hours) + ' ' + (duration.hours === 1 ? 'hour' : 'hours'));
}
if (duration.minutes !== 0) {
parts.push(numberFormatter.format(duration.minutes) + ' ' + (duration.minutes === 1 ? 'minute' : 'minutes'));
}
if (duration.seconds !== 0) {
// Round seconds for display if they have fractional parts
const roundedSeconds = numberFormatter.format(duration.seconds.toFixed(0)); // Or toFixed(1) for one decimal
parts.push(roundedSeconds + ' ' + (duration.seconds === 1 ? 'second' : 'seconds'));
}
if (parts.length === 0) {
// Handle cases where the duration is zero or very small (e.g., nanoseconds only)
if (duration.milliseconds !== 0 || duration.microseconds !== 0 || duration.nanoseconds !== 0) {
const totalMs = duration.milliseconds + duration.microseconds / 1000 + duration.nanoseconds / 1_000_000;
return numberFormatter.format(totalMs.toFixed(2)) + ' milliseconds';
}
return '0 seconds';
}
// Join parts with comma and 'and' for the last part (basic English joining)
if (parts.length > 1) {
const lastPart = parts.pop();
return parts.join(', ') + ' and ' + lastPart;
} else {
return parts[0];
}
}
const tripDuration = Temporal.Duration.from({ days: 3, hours: 10, minutes: 45 });
console.log(formatDurationToHumanReadable(tripDuration, 'en-US')); // 3 days, 10 hours and 45 minutes
console.log(formatDurationToHumanReadable(tripDuration, 'es-ES')); // 3 dĂas, 10 horas y 45 minutos (example of Intl.NumberFormat)
const meetingReminder = Temporal.Duration.from({ minutes: 5 });
console.log(formatDurationToHumanReadable(meetingReminder, 'en-GB')); // 5 minutes
const microDuration = Temporal.Duration.from({ nanoseconds: 1234567 });
console.log(formatDurationToHumanReadable(microDuration, 'en-US')); // 1.23 milliseconds
För mer avancerad pluralisering och lokaliserad listformatering kan du kombinera detta med Intl.RelativeTimeFormat eller mer komplexa bibliotek för strÀngmallar. Nyckeln Àr att separera berÀkningen av varaktigheten (hanteras av Temporal) frÄn dess presentation (hanteras av din formateringslogik och Intl).
AnvÀnda Intl.DurationFormat (framtid/förslag)
Det finns ett pÄgÄende TC39-förslag för Intl.DurationFormat, som syftar till att erbjuda ett inbyggt, sprÄkanpassat sÀtt att formatera varaktigheter. Om det standardiseras och implementeras skulle det avsevÀrt förenkla den manuella formateringen som visas ovan, och erbjuda en robust lösning för internationalisering direkt inom JavaScript-ekosystemet.
Det skulle troligen fungera pÄ liknande sÀtt som andra Intl-objekt:
// This is hypothetical and based on the proposal's current state
// Check browser compatibility before using in production
/*
const duration = Temporal.Duration.from({ days: 1, hours: 2, minutes: 30 });
const formatter = new Intl.DurationFormat('en-US', {
style: 'long',
years: 'long',
days: 'long',
hours: 'long',
minutes: 'long',
});
console.log(formatter.format(duration)); // "1 day, 2 hours, 30 minutes"
const shortFormatter = new Intl.DurationFormat('fr-FR', { style: 'short', hours: 'numeric', minutes: 'numeric' });
console.log(shortFormatter.format(duration)); // "1 j, 2 h, 30 min"
*/
Ăven om det Ă€nnu inte Ă€r tillgĂ€ngligt i alla miljöer, hĂ„ll ett öga pĂ„ detta förslag eftersom det lovar att bli den definitiva lösningen för global formatering av varaktigheter i framtiden.
Praktiska anvÀndningsfall och globala övervÀganden
Temporal.Duration Àr inte bara en akademisk förbÀttring; det löser verkliga problem i applikationer som verkar över olika tidszoner och kulturer.
1. SchemalÀggning och evenemangshantering
För plattformar som hanterar internationella evenemang, konferenser eller möten Àr det avgörande att berÀkna evenemangets varaktighet, tid kvar till ett evenemang eller hur lÀnge en paus varar. Temporal.Duration sÀkerstÀller att dessa berÀkningar Àr korrekta oavsett anvÀndarens plats eller lokala tidszonsregler.
// Calculate remaining time for a global webinar
const now = Temporal.ZonedDateTime.from('2024-07-25T10:00:00[Europe/London]'); // Example current time
const webinarStart = Temporal.ZonedDateTime.from('2024-07-26T14:30:00[Asia/Tokyo]');
const timeUntilWebinar = now.until(webinarStart, {
largestUnit: 'hour',
smallestUnit: 'minute',
roundingMode: 'ceil' // Round up to ensure users don't miss it
});
console.log(`Webinar starts in ${timeUntilWebinar.hours} hours and ${timeUntilWebinar.minutes} minutes.`);
// Output will be accurate based on actual time elapsed between these two ZonedDateTimes.
2. PrestandamÀtning och loggning
Att mÀta exekveringstiden för operationer, API-svarstider eller varaktigheten av batchjobb krÀver hög precision. Temporal.Duration, sÀrskilt i kombination med Temporal.Instant (som erbjuder nanosekundprecision), Àr idealiskt för detta.
const startTime = Temporal.Instant.now();
// Simulate a complex operation
for (let i = 0; i < 1_000_000; i++) { Math.sqrt(i); }
const endTime = Temporal.Instant.now();
const executionDuration = startTime.until(endTime);
// Format to seconds with milliseconds for display
const formattedExecution = executionDuration.round({ smallestUnit: 'millisecond', largestUnit: 'second' });
console.log(`Operation took ${formattedExecution.seconds}.${String(formattedExecution.milliseconds).padStart(3, '0')} seconds.`);
3. Finansiella berÀkningar
Inom finans Àr exakta tidsintervall av yttersta vikt för att berÀkna rÀnta, lÄnevillkor eller investeringsperioder. Det exakta antalet dagar, mÄnader eller Är i en period mÄste vara korrekt, sÀrskilt nÀr man hanterar skottÄr eller specifika mÄnadslÀngder.
const loanStartDate = Temporal.PlainDate.from('2023-04-01');
const loanEndDate = Temporal.PlainDate.from('2028-03-31');
const loanTerm = loanStartDate.until(loanEndDate, { largestUnit: 'year', smallestUnit: 'month' });
console.log(`Loan term: ${loanTerm.years} years and ${loanTerm.months} months.`); // 4 years and 11 months
4. Internationaliseringsutmaningar pÄ nytt
-
Tidszoner och sommartid:
Temporal.Durationi sig Àr tidszonsagnostiskt; det representerar en fast tidslÀngd. Men nÀr du adderar eller subtraherar enDurationtill/frÄn enZonedDateTime, tillÀmpar Temporal korrekt tidszonsregler, inklusive övergÄngar till sommartid. Detta sÀkerstÀller att en "24-timmars varaktighet" verkligen flyttar fram enZonedDateTimemed 24 faktiska timmar, Àven om det innebÀr att korsa en sommartidsgrÀns som gör *klockdagens* lÀngd kortare eller lÀngre. Denna konsekvens Àr en stor vinst över `Date`. -
Kulturella variationer: Olika kulturer uttrycker varaktigheter olika. Medan
Temporal.DurationtillhandahÄller de rÄa komponenterna (Är, mÄnader, dagar, etc.), Àr det ditt ansvar att anvÀnda `Intl`-API:er eller anpassad logik för att presentera dessa pÄ ett sÀtt som resonerar med anvÀndarens sprÄk och kultur. Till exempel kan vissa kulturer uttrycka "1 timme och 30 minuter" som "90 minuter" eller anvÀnda olika pluraliseringsregler. -
Kalendersystem: Temporal stöder ocksĂ„ olika kalendersystem (t.ex. japanska, persiska, islamiska kalendrar). Ăven om
Durationi sig Àr kalender-agnostiskt, respekterar aritmetiken den specifika kalenderns regler (t.ex. antal dagar i en mÄnad, skottÄrsregler inom den kalendern) nÀr den interagerar med kalendermedvetna typer somPlainDateellerZonedDateTime. Detta Àr avgörande för globala applikationer som kan behöva visa datum i icke-gregorianska kalendrar.
Temporals nuvarande status och anammande
I slutet av 2023/början av 2024 Àr JavaScript Temporal API ett Steg 3 TC39-förslag. Detta innebÀr att specifikationen Àr i stort sett stabil, och den genomgÄr implementering och testning i olika JavaScript-motorer och webblÀsare. Stora webblÀsare som Chrome, Firefox och Safari arbetar aktivt med att implementera Temporal, med experimentella flaggor eller tidiga versioner redan tillgÀngliga.
Ăven om det Ă€nnu inte Ă€r universellt tillgĂ€ngligt utan en polyfill, indikerar dess avancerade stadium att det Ă€r mycket troligt att det blir en standarddel av JavaScript. Utvecklare kan börja experimentera med Temporal idag genom att anvĂ€nda polyfills eller genom att aktivera experimentella funktioner i sina utvecklingsmiljöer i webblĂ€saren. Tidigt anammande lĂ„ter dig ligga steget före, förstĂ„ dess fördelar och integrera det i dina projekt allt eftersom webblĂ€sarstödet rullas ut.
Dess slutliga utbredda anammande kommer att avsevÀrt minska behovet av externa datum/tid-bibliotek och erbjuda en robust, inbyggd lösning för tidshantering i JavaScript.
BÀsta praxis för att anvÀnda Temporal Duration
För att maximera fördelarna med Temporal.Duration i dina applikationer, övervÀg dessa bÀsta praxis:
-
Föredra
Temporal.Duration.from(): NÀr du skapar varaktigheter frÄn kÀnda komponenter eller ISO-strÀngar ÀrTemporal.Duration.from()med en objektliteral ofta mer lÀsbar och mindre felbenÀgen Àn konstruktorns positionella argument. -
AnvÀnd
relativeToför tvetydiga jÀmförelser: Ange alltid ettrelativeTo-alternativ nÀr du jÀmför eller avrundar varaktigheter som innehÄller Är, mÄnader eller veckor. Detta sÀkerstÀller korrekta berÀkningar genom att ge den nödvÀndiga kalenderkontexten. -
Utnyttja
until()ochsince(): För att berÀkna intervallet mellan tvÄ specifika tidpunkter, föredra metodernauntil()ochsince()pÄ Temporal-datum/tidsobjekt. De hanterar komplexiteten med tidszoner och sommartid korrekt. -
Normalisera och avrunda för visning: Innan du presenterar varaktigheter för anvÀndare, övervÀg att anvÀnda
normalize()och sÀrskiltround()för att konvertera varaktigheten till de mest lÀmpliga och förstÄeliga enheterna (t.ex. konvertera 90 minuter till "1 timme och 30 minuter"). -
Separera intern representation frÄn visning: HÄll dina interna varaktighetsberÀkningar exakta med
Temporal.Duration. Omvandla och formatera endast för visning nÀr du renderar anvÀndargrÀnssnittet, med hjÀlp av anpassade formateringsfunktioner ochIntl-API:et för global noggrannhet. - HÄll dig uppdaterad: HÄll ett öga pÄ Temporal API:s framsteg och webblÀsarkompatibilitet. Allt eftersom det rör sig mot full standardisering kommer ekosystemet att utvecklas, och nya verktyg eller bÀsta praxis kan dyka upp.
- Var medveten om precision: Ăven om Temporal stöder nanosekundprecision, anvĂ€nd endast den precision du verkligen behöver. Högre precision kan ibland göra felsökning svĂ„rare eller resultera i ovĂ€ntad avrundning vid konvertering till displayer med lĂ€gre precision.
Slutsats
Introduktionen av Temporal.Duration markerar ett betydande steg framÄt för JavaScript-utvecklare som kÀmpar med tidsintervallaritmetik. Genom att tillhandahÄlla ett oförÀnderligt, explicit och globalt medvetet API, adresserar Temporal de lÄngvariga begrÀnsningarna hos det Àldre Date-objektet.
Du kan nu med sjÀlvförtroende utföra komplexa tidsberÀkningar, mÀta perioder noggrant och presentera varaktigheter pÄ ett sÀtt som Àr bÄde exakt och kulturellt lÀmpligt för anvÀndare över hela vÀrlden. Oavsett om du berÀknar varaktigheten av en mjukvaru-releasecykel, den ÄterstÄende tiden till en global produktlansering eller en persons exakta Älder, erbjuder Temporal.Duration de verktyg du behöver för att bygga robusta och pÄlitliga applikationer.
Omfamna Temporal.Duration och förvandla hur du hanterar tid i dina JavaScript-projekt. Framtiden för datum- och tidshantering i JavaScript Àr hÀr, och den lovar en mer förutsÀgbar, kraftfull och globalt kompatibel utvecklingsupplevelse.