Sukella JavaScript Temporal Durationiin, moderniin API:in tarkkaan aikaväliaritmetiikkaan, vertailuun ja muotoiluun. Opi hallitsemaan aikajaksoja luotettavasti ja globaalisti, välttäen Date-objektien yleiset sudenkuopat.
JavaScript Temporal Duration: Aikaväliaritmetiikan ja muotoilun hallinta globaaleissa sovelluksissa
Ajan hallinta ohjelmistokehityksessä on tunnetusti monimutkaista. Projektien aikataulujen seurannasta mantereiden välillä kansainvälisten videokonferenssien järjestämiseen, aikavälien, aikavyöhykkeiden ja kesäajan vivahteet voivat nopeasti johtaa hienovaraisiin mutta kriittisiin virheisiin. Vuosikymmenten ajan JavaScript-kehittäjät ovat kamppailleet sisäänrakennetun Date-objektin kanssa, joka on työkalu, joka, vaikka se on toimiva yksinkertaisille aika-pisteille, ei riitä tarkkaan aikaväliaritmetiikkaan ja vankkaan, globaalisti tietoiseen ajanhallintaan.
Tässä tulee esiin JavaScript Temporal API – mullistava ehdotus, joka on suunniteltu tarjoamaan moderni, vankka ja käyttäjäystävällinen API päivämäärien ja aikojen käsittelyyn JavaScriptissä. Sen tehokkaiden uusien tyyppien joukossa Temporal.Duration erottuu lopullisena ratkaisuna aikavälien käsittelyyn. Tämä artikkeli vie sinut syvälle Temporal.Duration-maailmaan, tutkien sen kykyjä aritmetiikassa, vertailussa ja älykkäässä muotoilussa, varmistaen, että sovelluksesi voivat hallita aikaa globaalilla tarkkuudella ja selkeydellä.
Olitpa sitten rakentamassa globaalia logistiikkajärjestelmää, rahoituskaupankäyntialustaa tai monen aikavyöhykkeen tapahtumasuunnittelijaa, Temporal.Duration-tyypin ymmärtäminen on ratkaisevan tärkeää aikaan liittyvien epäselvyyksien poistamiseksi ja luotettavien, kansainvälistettyjen käyttäjäkokemusten tuottamiseksi.
JavaScriptin Date-objektin riittämättömyys aikaväleille
Ennen kuin juhlimme Temporal.Duration-tyypin saapumista, on tärkeää ymmärtää olemassa olevan Date-objektin rajoitukset, erityisesti aikavälien käsittelyssä. Date-objekti edustaa tiettyä ajanhetkeä, joka mitataan millisekunteina Unix-epochista (1. tammikuuta 1970, UTC). Vaikka sitä voidaan käyttää perusaritmetiikan suorittamiseen, siinä on useita luontaisia puutteita, jotka tekevät siitä sopimattoman vankkaan kestonhallintaan:
-
Muuttuvuus:
Date-objektit ovat muuttuvia. Mikä tahansa operaatioDate-objektilla muuttaa sen sisäistä tilaa, mikä voi johtaa odottamattomiin sivuvaikutuksiin ja vaikeasti jäljitettäviin virheisiin, erityisesti monimutkaisissa sovelluksissa tai rinnakkaisissa ympäristöissä.const d = new Date('2023-01-15T10:00:00Z'); const d2 = d; // d2 viittaa nyt samaan objektiin kuin 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 myös muuttui!) -
Kestokäsitteen puute:
Date-objektilla ei ole suoraa käsitettä "kestosta" tai "ajanjaksosta". Kahden päivämäärän välisen eron laskeminen tuottaa millisekuntien määrän, joka on sitten manuaalisesti muunnettava vuosiksi, kuukausiksi, päiviksi jne. Tämä manuaalinen muunnos on altis virheille, erityisesti käsiteltäessä vaihtelevan pituisia kuukausia tai karkausvuosia.const date1 = new Date('2023-01-01T00:00:00Z'); const date2 = new Date('2023-03-01T00:00:00Z'); const diffMs = date2.getTime() - date1.getTime(); // Kuinka monta kuukautta tämä on? Entä karkausvuodet? // (diffMs / (1000 * 60 * 60 * 24 * 30)) on parhaimmillaankin vain arvio. console.log(`Difference in milliseconds: ${diffMs}`); console.log(`Approximate days: ${diffMs / (1000 * 60 * 60 * 24)}`); // Toimii päiville, mutta ei ole luotettava kuukausille/vuosille -
Aikavyöhykkeen epäselvyys:
Date-objektit sekoittavat usein paikallisen ajan ja UTC-ajan. Vaikka ne tallentavat sisäisesti UTC-millisekunteja, niiden metodit toimivat oletusarvoisesti järjestelmän paikallisella aikavyöhykkeellä, mikä johtaa sekaannuksiin ja epäjohdonmukaisuuksiin työskenneltäessä hajautettujen järjestelmien tai kansainvälisten käyttäjien kanssa. - Kesäajan (DST) haasteet: Kesäaikasiirtymät voivat aiheuttaa sen, että päivät ovat 23 tai 25 tuntia pitkiä. Yksinkertainen aritmetiikka (esim. 24 tunnin lisääminen päivämäärään) ei välttämättä aina johda seuraavaan kalenteripäivään, mikä johtaa virheellisiin laskelmiin, kun "päivän" oletetaan olevan kiinteä 24 tunnin jakso.
Nämä rajoitukset ovat historiallisesti pakottaneet kehittäjät turvautumaan kolmannen osapuolen kirjastoihin, kuten Moment.js tai date-fns, tai kirjoittamaan monimutkaista, virhealtista omaa koodia aikavälien oikeaan käsittelyyn. Temporal pyrkii tuomaan nämä ominaisuudet natiivisti JavaScriptiin.
Esittelyssä JavaScript Temporal: Moderni lähestymistapa aikaan
Temporal API on kattava, uusi globaali objekti JavaScriptissä, joka on suunniteltu moderniksi korvaajaksi vanhalle Date-objektille. Sen ydinperiaatteita ovat muuttumattomuus, eksplisiittinen aikavyöhykkeen käsittely ja selkeä vastuunjako eri aikakäsitteiden välillä. Temporal esittelee useita uusia luokkia, joista jokainen edustaa tiettyä ajan osa-aluetta:
Temporal.Instant: Tietty, yksiselitteinen ajanhetki, riippumaton kalenterista tai aikavyöhykkeestä (vastaa Unix-aikaleimaa, mutta nanosekunnin tarkkuudella).Temporal.ZonedDateTime: Tietty ajanhetki tietyssä kalenterissa ja aikavyöhykkeessä. Tämä on täydellisin esitys tietystä päivämäärästä ja ajasta käyttäjälle.Temporal.PlainDate: Kalenteripäivämäärä (vuosi, kuukausi, päivä) ilman aikaa tai aikavyöhykettä.Temporal.PlainTime: Kellonaika (tunti, minuutti, sekunti jne.) ilman päivämäärää tai aikavyöhykettä.Temporal.PlainDateTime: Kalenteripäivämäärä ja kellonaika yhdessä, ilman aikavyöhykettä.Temporal.PlainYearMonth: Tietty vuosi ja kuukausi kalenterijärjestelmässä.Temporal.PlainMonthDay: Tietty kuukausi ja päivä kalenterijärjestelmässä.Temporal.Duration: Merkitty ajan pituus, kuten "5 tuntia ja 30 minuuttia" tai "2 päivää". Tämä on oppaamme keskiössä.
Kaikki Temporal-objektit ovat muuttumattomia, mikä tarkoittaa, että operaatiot kuten ajan lisääminen tai vähentäminen luovat uusia objekteja sen sijaan, että ne muokkaisivat olemassa olevia. Tämä parantaa ennustettavuutta ja vähentää virheitä.
Temporal.Duration-tyypin ymmärtäminen
Temporal.Duration edustaa ajan pituutta. Ratkaisevaa on, että se on riippumaton tietystä alku- tai loppupisteestä. Se on yksinkertaisesti "kuinka paljon aikaa" kului tai tulee kulumaan. Se voi koostua vuosista, kuukausista, viikoista, päivistä, tunneista, minuuteista, sekunneista, millisekunneista, mikrosekunneista ja nanosekunneista. Jokainen komponentti on kokonaisluku ja voi olla positiivinen tai negatiivinen.
Esimerkiksi "2 tuntia ja 30 minuuttia" on kesto. "Ajanjakso tammikuun 1. päivästä maaliskuun 1. päivään" on kesto kahden tietyn pisteen välillä, jota voidaan *edustaa* Temporal.Duration-tyypillä, mutta Duration itsessään on vain aikaväli.
Keston luominen
Temporal.Duration-objektien luomiseen on useita suoraviivaisia tapoja:
1. Konstruktorin käyttö
Konstruktorin avulla voit määrittää jokaisen komponentin suoraan. Huomaa, että argumentit ovat järjestyksessä suurimmasta yksiköstä (vuodet) pienimpään (nanosekunnit).
// new Temporal.Duration(years, months, weeks, days, hours, minutes, seconds, milliseconds, microseconds, nanoseconds)
// 2 tunnin ja 30 minuutin kesto
const duration1 = new Temporal.Duration(0, 0, 0, 0, 2, 30, 0, 0, 0, 0);
console.log(duration1.toString()); // P2H30M
// 1 vuoden, 2 kuukauden ja 3 päivän kesto
const duration2 = new Temporal.Duration(1, 2, 0, 3);
console.log(duration2.toString()); // P1Y2M3D
// -5 päivän kesto
const duration3 = new Temporal.Duration(0, 0, 0, -5);
console.log(duration3.toString()); // P-5D
2. Temporal.Duration.from()-metodin käyttö objektin kanssa
Tämä on usein luettavin tapa luoda kestoja, koska voit määrittää vain tarvitsemasi komponentit.
// 1,5 tunnin kesto
const halfHourDuration = Temporal.Duration.from({ hours: 1, minutes: 30 });
console.log(halfHourDuration.toString()); // P1H30M
// 7 päivän kesto (1 viikko)
const oneWeekDuration = Temporal.Duration.from({ days: 7 });
console.log(oneWeekDuration.toString()); // P7D
// Kesto murtolukusekunneilla (esim. 2,5 sekuntia)
const twoPointFiveSeconds = Temporal.Duration.from({ seconds: 2, milliseconds: 500 });
console.log(twoPointFiveSeconds.toString()); // PT2.5S
// Negatiivinen kesto
const negativeDuration = Temporal.Duration.from({ minutes: -45 });
console.log(negativeDuration.toString()); // PT-45M
3. Temporal.Duration.from()-metodin käyttö ISO 8601 -merkkijonon kanssa
Temporal hyödyntää ISO 8601 -kestomuotoa, joka on standardi kestojen esittämiseen. Tämä on erinomaista kestojen jäsentämiseen ulkoisista tietolähteistä.
Muoto näyttää yleensä tältä: P[years]Y[months]M[weeks]W[days]DT[hours]H[minutes]M[seconds]S. T erottaa päivämääräkomponentit aikakomponenteista.
// 1 vuosi, 2 kuukautta, 3 päivää
const isoDuration1 = Temporal.Duration.from('P1Y2M3D');
console.log(isoDuration1.toString()); // P1Y2M3D
// 4 tuntia, 5 minuuttia, 6 sekuntia
const isoDuration2 = Temporal.Duration.from('PT4H5M6S');
console.log(isoDuration2.toString()); // PT4H5M6S
// Yhdistetty kesto
const isoDuration3 = Temporal.Duration.from('P7DT12H30M');
console.log(isoDuration3.toString()); // P7DT12H30M
// Myös murtolukusekunnit ovat tuettuja
const isoDuration4 = Temporal.Duration.from('PT1.5S');
console.log(isoDuration4.toString()); // PT1.5S
Aritmetiikan suorittaminen kestoilla
Temporal.Duration-tyypin todellinen voima piilee sen aritmeettisissa kyvyissä. Voit lisätä, vähentää, kertoa ja jakaa kestoja sekä lisätä/vähentää niitä muista Temporal-aikaobjekteista. Kaikki operaatiot palauttavat uusia Temporal.Duration-objekteja muuttumattomuuden ansiosta.
Kestojen lisääminen
add()-metodi yhdistää kaksi kestoa.
const sprintDuration = Temporal.Duration.from({ weeks: 2 });
const bufferDuration = Temporal.Duration.from({ days: 3 });
const totalProjectTime = sprintDuration.add(bufferDuration);
console.log(totalProjectTime.toString()); // P2W3D (tai P17D myöhemmin normalisoituna)
// Negatiivisen keston lisääminen vastaa vähennyslaskua
const result = Temporal.Duration.from({ hours: 5 }).add({ hours: -2 });
console.log(result.toString()); // PT3H
Voit myös lisätä keston mihin tahansa Temporal-aikaobjektiin. Tässä taika tapahtuu, sillä Temporal käsittelee aikavyöhykemuutokset ja kesäaikasiirtymät oikein tarvittaessa.
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
// ZonedDateTime-tyypin kanssa aikavyöhykesääntöjä sovelletaan oikein
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]
// Esimerkki kesäaikarajan ylityksestä (olettaen, että 'Europe/Berlin' siirtyy klo 03:00 31.3.2024)
const springForwardStart = Temporal.ZonedDateTime.from('2024-03-30T22:00:00[Europe/Berlin]');
const twentyFourHours = Temporal.Duration.from({ hours: 24 });
const nextDay = springForwardStart.add(twentyFourHours); // Lisää 24 todellista tuntia
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] (Paikallinen aika hyppäsi tunnin eteenpäin)
Huomaa, kuinka 24 tunnin lisääminen aikaan 2024-03-30T22:00:00 Berliinissä (joka on UTC+1) johtaa aikaan 2024-03-31T23:00:00 (nyt UTC+2). Kello siirtyi eteenpäin tunnilla, joten kellonaika on tunnin myöhemmin samana päivänä suhteessa lähtöajan kellonaikaan. Tämä osoittaa tarkasti Temporalin aikavyöhyke- ja kesäaikatietoisuuden suoritettaessa aritmetiikkaa ZonedDateTime-tyypillä.
Kestojen vähentäminen
subtract()-metodi toimii samalla tavalla kuin add(), mutta se vähentää aikaa.
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
Kestojen kertominen ja jakaminen
multiply()- ja divide()-metodit skaalaavat keston komponentteja annetulla kertoimella. Tämä on hyödyllistä esimerkiksi laskettaessa kokonaisaikaa tehtävän useille toistoille.
const trainingSession = Temporal.Duration.from({ minutes: 45 });
const weeklyTraining = trainingSession.multiply(5); // Viisi harjoitusta viikossa
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
Kestojen negaatio
negate()-metodi kääntää kaikkien keston komponenttien etumerkin. Positiivisesta kestosta tulee negatiivinen ja päinvastoin.
const delayDuration = Temporal.Duration.from({ hours: 3 });
const advanceDuration = delayDuration.negate();
console.log(delayDuration.toString()); // PT3H
console.log(advanceDuration.toString()); // PT-3H
Kestojen itseisarvo
abs()-metodi palauttaa uuden Temporal.Duration-objektin, jonka kaikki komponentit on muutettu positiivisiksi, antaen tehokkaasti keston suuruuden sen etumerkistä riippumatta.
const negativeDelay = Temporal.Duration.from({ minutes: -60 });
const positiveDuration = negativeDelay.abs();
console.log(negativeDelay.toString()); // PT-60M
console.log(positiveDuration.toString()); // PT60M
Kestojen vertailu ja normalisointi
Kestojen vertailu voi olla hankalaa, erityisesti kun mukana on eri yksiköitä (esim. onko 1 kuukausi sama kuin 30 päivää?). Temporal tarjoaa työkaluja sekä vertailuun että normalisointiin näiden monimutkaisuuksien käsittelemiseksi.
Kestojen vertailu compare()-metodilla
Staattinen Temporal.Duration.compare(duration1, duration2, options) -metodi palauttaa:
-1, josduration1on pienempi kuinduration20, josduration1on yhtä suuri kuinduration21, josduration1on suurempi kuinduration2
Ratkaisevaa on, että verrattaessa kestoja, jotka sisältävät vaihtelevan pituisia yksiköitä kuten vuosia, kuukausia tai viikkoja, sinun on usein annettava relativeTo-optio. Tämä parametri on `Temporal.ZonedDateTime`- tai `Temporal.PlainDateTime`-objekti, joka tarjoaa kontekstin näiden yksiköiden tulkitsemiseen (esim. kuinka monta päivää on tietyssä kuukaudessa tai vuodessa).
const oneHour = Temporal.Duration.from({ hours: 1 });
const sixtyMinutes = Temporal.Duration.from({ minutes: 60 });
console.log(Temporal.Duration.compare(oneHour, sixtyMinutes)); // 0 (Ne ovat vastaavat)
const oneMonth = Temporal.Duration.from({ months: 1 });
const thirtyDays = Temporal.Duration.from({ days: 30 });
// Ilman relativeTo-optiota kuukausi/vuosi-vertailut ovat vaikeita
console.log(Temporal.Duration.compare(oneMonth, thirtyDays)); // 0 (Temporal tekee järkevän arvauksen ilman kontekstia, usein perustuen keskiarvoon)
// relativeTo-option avulla vertailu on tarkka kontekstin kalenterin perusteella
const startOfJanuary = Temporal.PlainDate.from('2023-01-01');
const endOfFebruaryLeap = Temporal.PlainDate.from('2024-02-01'); // Karkausvuosi
// Tammikuussa 2023, 1 kuukausi on 31 päivää
const comparisonJan = Temporal.Duration.compare(oneMonth, thirtyDays, { relativeTo: startOfJanuary });
console.log(`1 month vs 30 days in Jan 2023: ${comparisonJan}`); // 1 (1 kuukausi > 30 päivää)
// Helmikuussa 2024 (karkausvuosi), 1 kuukausi on 29 päivää
const comparisonFeb = Temporal.Duration.compare(oneMonth, thirtyDays, { relativeTo: endOfFebruaryLeap });
console.log(`1 month vs 30 days in Feb 2024: ${comparisonFeb}`); // -1 (1 kuukausi < 30 päivää)
Kestojen normalisointi normalize()- ja round()-metodeilla
Kestoja voidaan esittää monin tavoin (esim. 90 minuuttia tai 1 tunti ja 30 minuuttia). Normalisointi ja pyöristäminen auttavat standardoimaan näitä esityksiä johdonmukaisuuden ja näytön vuoksi.
normalize()
normalize()-metodi yksinkertaistaa keston komponentteja mahdollisuuksien mukaan (esim. 60 minuutista tulee 1 tunti, 24 tunnista tulee 1 päivä, edellyttäen että `relativeTo`-konteksti sallii sen, jos mukana on kuukausia/vuosia).
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()-metodi on tehokkaampi kestojen muuntamiseen ja pyöristämiseen tiettyihin yksiköihin. Se ottaa vastaan options-objektin, jossa on:
largestUnit: Suurin tulosteeseen sisällytettävä yksikkö (esim. 'years', 'days', 'hours').smallestUnit: Pienin tulosteeseen sisällytettävä yksikkö (esim. 'minutes', 'seconds', 'milliseconds').roundingIncrement: Kokonaisluku, jonka mukaan pienin yksikkö pyöristetään (esim. 5 lähimpään 5 minuuttiin pyöristämiseksi).roundingMode: Miten tasapelit käsitellään (esim. 'halfExpand', 'trunc', 'ceil', 'floor').relativeTo: Vaaditaan, kun pyöristetään kestoja, jotka sisältävät vuosia, kuukausia tai viikkoja.
const complexDuration = Temporal.Duration.from({ hours: 2, minutes: 45, seconds: 30 });
// Pyöristä lähimpään tuntiin
const roundedToHours = complexDuration.round({ smallestUnit: 'hour' });
console.log(roundedToHours.toString()); // PT3H
// Pyöristä lähimpään 30 minuuttiin, säilyttäen tunnit
const roundedTo30Minutes = complexDuration.round({
largestUnit: 'hour',
smallestUnit: 'minute',
roundingIncrement: 30
});
console.log(roundedTo30Minutes.toString()); // PT3H
const preciseDuration = Temporal.Duration.from({ minutes: 123, seconds: 45 });
// Näytä tunteina ja minuutteina
const formattedDuration = preciseDuration.round({ largestUnit: 'hour', smallestUnit: 'minute' });
console.log(formattedDuration.toString()); // PT2H4M
// Kuukausien/vuosien pyöristäminen vaatii relativeTo-option
const longTermDuration = Temporal.Duration.from({ months: 1, days: 10 });
const referenceDate = Temporal.PlainDate.from('2023-01-15');
// Pyöristä kuukausiin, suhteessa päivämäärään
const roundedToMonths = longTermDuration.round({ largestUnit: 'month', smallestUnit: 'month', relativeTo: referenceDate });
console.log(roundedToMonths.toString()); // P1M
Kestojen laskeminen Temporal-objektien välillä
Yksi yleisimmistä kestojen käyttötarkoituksista on aikavälin laskeminen kahden tietyn ajanhetken välillä. Temporal tarjoaa tähän tarkoitukseen until()- ja since()-metodit aikaobjekteissaan.
until()-metodi
until()-metodi laskee keston vastaanottajaobjektista argumenttiobjektiin. Se sisältää alkupisteen mutta ei loppupistettä. Se ottaa vastaan round()-metodin kaltaisen options-objektin haluttujen yksiköiden ja pyöristyskäyttäytymisen määrittämiseksi.
const startDate = Temporal.PlainDate.from('2023-01-01');
const endDate = Temporal.PlainDate.from('2023-03-15');
// Kesto suurimmissa mahdollisissa yksiköissä (kuukaudet, sitten päivät)
const projectLength = startDate.until(endDate);
console.log(projectLength.toString()); // P2M14D
// Kesto pelkästään päivinä
const totalDays = startDate.until(endDate, { largestUnit: 'day' });
console.log(totalDays.toString()); // P73D
// Kesto kahden tietyn ajan välillä, aikavyöhykkeet huomioiden
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
// Aikavyöhykkeiden ylittävä kesto (NYC:stä Lontooseen)
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 (Todellinen kulunut aika, ei kellonaikojen erotus)
Viimeinen esimerkki on erityisen oivaltava. Vaikka New York on 5 tuntia Lontoon jäljessä ja kellonajat ovat 9 AM ja 5 PM samana päivänä, until()-metodi laskee oikein todellisen kuluneen ajan, 13 tuntia. Tämä johtuu siitä, että ZonedDateTime käsittelee aikavyöhyke-eron implisiittisesti.
since()-metodi
since()-metodi on until()-metodin käänteistoiminto. Se laskee keston argumenttiobjektista vastaanottajaobjektiin, mikä johtaa negatiiviseen kestoon, jos argumentti on tulevaisuudessa suhteessa vastaanottajaan.
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
Eri yksiköiden ja pyöristyksen käsittely lasketuille kestoille
Kestoja laskettaessa on usein tarpeen määrittää `largestUnit` ja `smallestUnit` saadaksesi ihmisluettavan esityksen, erityisesti iän, kuluneen ajan tai lähtölaskennan tapauksessa.
const birthDate = Temporal.PlainDate.from('1990-07-15');
const today = Temporal.PlainDate.from('2024-06-15');
// Laske ikä vuosina, kuukausina ja päivinä
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
// Laske jäljellä oleva aika tehtävälle tunteina ja minuutteina
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.
Kestojen muotoilu globaaleille yleisöille
Vaikka Temporal.Duration tarjoaa tarkkoja, ohjelmallisia esityksiä aikaväleistä, sillä ei ole sisäänrakennettua toLocaleString()-metodia. Tämä on suunniteltua: kestot ovat abstrakteja ajan pituuksia, ja niiden näyttötapa voi vaihdella suuresti kontekstin, kieli- ja maa-asetusten sekä halutun yksityiskohtaisuuden tason mukaan. Sinä kehittäjänä olet vastuussa kestojen esittämisestä käyttäjäystävällisellä, globaalisti ymmärrettävällä tavalla.
ISO 8601 -merkkijonoesitys
Temporal.Duration-objektin oletusarvoinen toString()-metodi palauttaa sen ISO 8601 -merkkijonoesityksen. Tämä on erinomaista koneiden väliseen viestintään, sarjallistamiseen ja tallentamiseen, mutta harvoin suoraan loppukäyttäjille näytettäväksi.
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
Manuaalinen muotoilu luettavuuden ja kansainvälistämisen vuoksi
Käyttäjälle näytettävää esitystä varten tyypillisesti purat keston komponentit ja muotoilet ne merkkijonojen interpoloinnin ja JavaScriptin Intl API:n avulla.
Tässä on esimerkki omasta funktiosta, joka muotoilee keston:
function formatDurationToHumanReadable(duration, locale = 'fi-FI') {
const parts = [];
// Käytetään Intl.NumberFormatia kielikohtaiseen numeronmuotoiluun
const numberFormatter = new Intl.NumberFormat(locale);
// Suomenkieliset yksiköt
const units = {
year: { one: 'vuosi', other: 'vuotta' },
month: { one: 'kuukausi', other: 'kuukautta' },
week: { one: 'viikko', other: 'viikkoa' },
day: { one: 'päivä', other: 'päivää' },
hour: { one: 'tunti', other: 'tuntia' },
minute: { one: 'minuutti', other: 'minuuttia' },
second: { one: 'sekunti', other: 'sekuntia' }
};
if (duration.years !== 0) {
parts.push(numberFormatter.format(duration.years) + ' ' + (duration.years === 1 ? units.year.one : units.year.other));
}
if (duration.months !== 0) {
parts.push(numberFormatter.format(duration.months) + ' ' + (duration.months === 1 ? units.month.one : units.month.other));
}
if (duration.weeks !== 0) {
parts.push(numberFormatter.format(duration.weeks) + ' ' + (duration.weeks === 1 ? units.week.one : units.week.other));
}
if (duration.days !== 0) {
parts.push(numberFormatter.format(duration.days) + ' ' + (duration.days === 1 ? units.day.one : units.day.other));
}
if (duration.hours !== 0) {
parts.push(numberFormatter.format(duration.hours) + ' ' + (duration.hours === 1 ? units.hour.one : units.hour.other));
}
if (duration.minutes !== 0) {
parts.push(numberFormatter.format(duration.minutes) + ' ' + (duration.minutes === 1 ? units.minute.one : units.minute.other));
}
if (duration.seconds !== 0) {
const roundedSeconds = numberFormatter.format(duration.seconds.toFixed(0));
parts.push(roundedSeconds + ' ' + (duration.seconds === 1 ? units.second.one : units.second.other));
}
if (parts.length === 0) {
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)) + ' millisekuntia';
}
return '0 sekuntia';
}
// Osien yhdistäminen suomen kielen sääntöjen mukaan
if (parts.length > 1) {
const lastPart = parts.pop();
return parts.join(', ') + ' ja ' + lastPart;
} else {
return parts[0];
}
}
const tripDuration = Temporal.Duration.from({ days: 3, hours: 10, minutes: 45 });
console.log(formatDurationToHumanReadable(tripDuration, 'fi-FI')); // 3 päivää, 10 tuntia ja 45 minuuttia
const meetingReminder = Temporal.Duration.from({ minutes: 5 });
console.log(formatDurationToHumanReadable(meetingReminder, 'fi-FI')); // 5 minuuttia
const microDuration = Temporal.Duration.from({ nanoseconds: 1234567 });
console.log(formatDurationToHumanReadable(microDuration, 'fi-FI')); // 1,23 millisekuntia
Edistyneempää monikkomuotojen ja lokalisoitujen listojen muotoilua varten voitaisiin yhdistää tämä Intl.RelativeTimeFormat-olioon tai monimutkaisempiin merkkijonopohjaisiin kirjastoihin. Tärkeintä on erottaa keston laskenta (jonka hoitaa Temporal) sen esittämisestä (jonka hoitaa muotoilulogiikkasi ja Intl).
Intl.DurationFormat-olion käyttö (Tulevaisuus/Ehdotus)
On olemassa jatkuva TC39-ehdotus Intl.DurationFormat-oliolle, joka pyrkii tarjoamaan natiivin, kielikohtaisen tavan muotoilla kestoja. Jos se standardoidaan ja toteutetaan, se yksinkertaistaisi huomattavasti yllä esitettyä manuaalista muotoilua ja tarjoaisi vankan ratkaisun kansainvälistämiseen suoraan JavaScript-ekosysteemissä.
Se toimisi todennäköisesti samalla tavalla kuin muut Intl-oliot:
// Tämä on hypoteettista ja perustuu ehdotuksen nykytilaan
// Tarkista selainyhteensopivuus ennen tuotantokäyttöä
/*
const duration = Temporal.Duration.from({ days: 1, hours: 2, minutes: 30 });
const formatter = new Intl.DurationFormat('fi-FI', {
style: 'long',
years: 'long',
days: 'long',
hours: 'long',
minutes: 'long',
});
console.log(formatter.format(duration)); // "1 päivä, 2 tuntia ja 30 minuuttia"
const shortFormatter = new Intl.DurationFormat('fi-FI', { style: 'short', hours: 'numeric', minutes: 'numeric' });
console.log(shortFormatter.format(duration)); // "1 p, 2 t, 30 min"
*/
Vaikka se ei ole vielä saatavilla kaikissa ympäristöissä, kannattaa pitää silmällä tätä ehdotusta, sillä se lupaa olla tulevaisuudessa lopullinen ratkaisu globaaliin keston muotoiluun.
Käytännön käyttötapaukset ja globaalit näkökohdat
Temporal.Duration ei ole vain akateeminen parannus; se ratkaisee todellisia ongelmia sovelluksissa, jotka toimivat eri aikavyöhykkeillä ja kulttuureissa.
1. Aikataulutus ja tapahtumien hallinta
Kansainvälisiä tapahtumia, konferensseja tai tapaamisia hallinnoivilla alustoilla tapahtumien kestojen, tapahtumaan jäljellä olevan ajan tai tauon pituuden laskeminen on kriittistä. Temporal.Duration varmistaa, että nämä laskelmat ovat tarkkoja riippumatta käyttäjän sijainnista tai paikallisista aikavyöhykesäännöistä.
// Laske jäljellä oleva aika globaaliin webinaariin
const now = Temporal.ZonedDateTime.from('2024-07-25T10:00:00[Europe/London]'); // Esimerkki nykyisestä ajasta
const webinarStart = Temporal.ZonedDateTime.from('2024-07-26T14:30:00[Asia/Tokyo]');
const timeUntilWebinar = now.until(webinarStart, {
largestUnit: 'hour',
smallestUnit: 'minute',
roundingMode: 'ceil' // Pyöristä ylöspäin varmistaaksesi, etteivät käyttäjät myöhästy
});
console.log(`Webinaari alkaa ${timeUntilWebinar.hours} tunnin ja ${timeUntilWebinar.minutes} minuutin kuluttua.`);
// Tuloste on tarkka perustuen todelliseen kuluneeseen aikaan näiden kahden ZonedDateTime-objektin välillä.
2. Suorituskykymittarit ja lokitiedot
Operaatioiden suoritusajan, API-vastausaikojen tai eräajojen kestojen mittaaminen vaatii suurta tarkkuutta. Temporal.Duration, erityisesti yhdistettynä Temporal.Instant-tyyppiin (joka tarjoaa nanosekunnin tarkkuuden), on ihanteellinen tähän.
const startTime = Temporal.Instant.now();
// Simuloidaan monimutkaista operaatiota
for (let i = 0; i < 1_000_000; i++) { Math.sqrt(i); }
const endTime = Temporal.Instant.now();
const executionDuration = startTime.until(endTime);
// Muotoile sekunneiksi millisekuntien kanssa näyttöä varten
const formattedExecution = executionDuration.round({ smallestUnit: 'millisecond', largestUnit: 'second' });
console.log(`Operaatio kesti ${formattedExecution.seconds}.${String(formattedExecution.milliseconds).padStart(3, '0')} sekuntia.`);
3. Rahoituslaskelmat
Rahoitusalalla tarkat aikavälit ovat ensisijaisen tärkeitä korkojen, laina-aikojen tai sijoitusjaksojen laskemisessa. Tarkan päivien, kuukausien tai vuosien määrän on oltava tarkka, erityisesti käsiteltäessä karkausvuosia tai tiettyjen kuukausien pituuksia.
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(`Laina-aika: ${loanTerm.years} vuotta ja ${loanTerm.months} kuukautta.`); // 4 vuotta ja 11 kuukautta
4. Kansainvälistämisen haasteet uudelleen tarkasteltuna
-
Aikavyöhykkeet ja kesäaika:
Temporal.Durationitsessään on aikavyöhykkeestä riippumaton; se edustaa kiinteää ajan pituutta. Kuitenkin, kun lisäät tai vähennätDuration-objektinZonedDateTime-objektiin tai -objektista, Temporal soveltaa oikein aikavyöhykesääntöjä, mukaan lukien kesäaikasiirtymät. Tämä varmistaa, että "24 tunnin kesto" todella siirtääZonedDateTime-objektia 24 todellista tuntia eteenpäin, vaikka se ylittäisikin kesäaikarajan, joka tekee *kellon* päivästä lyhyemmän tai pidemmän. Tämä johdonmukaisuus on suuri voittoDate-objektiin verrattuna. -
Kulttuuriset vaihtelut: Eri kulttuurit ilmaisevat kestoja eri tavoin. Vaikka
Temporal.Durationtarjoaa raa'at komponentit (vuodet, kuukaudet, päivät jne.), on sinun vastuullasi käyttää `Intl`-API:ita tai omaa logiikkaa esittääksesi ne tavalla, joka resonoi käyttäjän kieli- ja maa-asetusten kanssa. Esimerkiksi jotkut kulttuurit saattavat ilmaista "1 tunti ja 30 minuuttia" muodossa "90 minuuttia" tai käyttää erilaisia monikkomuotojen sääntöjä. -
Kalenterijärjestelmät: Temporal tukee myös erilaisia kalenterijärjestelmiä (esim. japanilainen, persialainen, islamilainen kalenteri). Vaikka
Durationitsessään on kalenterista riippumaton, kun se on vuorovaikutuksessa kalenteritietoisten tyyppien, kutenPlainDatetaiZonedDateTime, kanssa, aritmetiikka noudattaa kyseisen kalenterin sääntöjä (esim. päivien lukumäärä kuukaudessa, karkausvuosisäännöt kyseisessä kalenterissa). Tämä on ratkaisevan tärkeää globaaleille sovelluksille, joiden saattaa olla tarpeen näyttää päivämääriä ei-gregoriaanisissa kalentereissa.
Temporalin nykytila ja käyttöönotto
Vuoden 2023 lopulla / 2024 alussa JavaScript Temporal API on Stage 3 TC39 -ehdotus. Tämä tarkoittaa, että määrittely on suurelta osin vakaa, ja sitä toteutetaan ja testataan eri JavaScript-moottoreissa ja selaimissa. Suurimmat selaimet, kuten Chrome, Firefox ja Safari, työskentelevät aktiivisesti Temporalin toteuttamiseksi, ja kokeelliset liput tai varhaiset versiot ovat jo saatavilla.
Vaikka se ei ole vielä yleisesti saatavilla ilman polyfilliä, sen edistynyt vaihe osoittaa, että siitä tulee hyvin todennäköisesti JavaScriptin vakiintunut osa. Kehittäjät voivat aloittaa Temporalin kokeilun jo tänään käyttämällä polyfillejä tai ottamalla käyttöön kokeellisia ominaisuuksia selainkehitysympäristöissään. Varhainen käyttöönotto antaa sinulle etumatkaa, auttaa ymmärtämään sen hyötyjä ja integroimaan sen projekteihisi selainten tuen laajentuessa.
Sen lopullinen laaja käyttöönotto vähentää merkittävästi tarvetta ulkoisille aika- ja päivämääräkirjastoille, tarjoten vankan, natiivin ratkaisun JavaScriptin ajanhallintaan.
Parhaat käytännöt Temporal Durationin käyttöön
Maksimoidaksesi Temporal.Duration-tyypin hyödyt sovelluksissasi, harkitse näitä parhaita käytäntöjä:
-
Suosi
Temporal.Duration.from()-metodia: Kun luot kestoja tunnetuista komponenteista tai ISO-merkkijonoista,Temporal.Duration.from()objektiliteraalin kanssa on usein luettavampi ja vähemmän virhealtis kuin konstruktorin sijaintiin perustuvat argumentit. -
Käytä
relativeTo-optiota epäselvissä vertailuissa: Anna ainarelativeTo-optio, kun vertailet tai pyöristät kestoja, jotka sisältävät vuosia, kuukausia tai viikkoja. Tämä varmistaa tarkat laskelmat tarjoamalla tarvittavan kalenterikontekstin. -
Hyödynnä
until()- jasince()-metodeja: Kahden tietyn ajanhetken välisen aikavälin laskemiseen suosi Temporal-aikaobjektienuntil()- jasince()-metodeja. Ne käsittelevät aikavyöhykkeiden ja kesäajan monimutkaisuudet oikein. -
Normalisoi ja pyöristä näyttöä varten: Ennen kuin esität kestoja käyttäjille, harkitse
normalize()- ja erityisestiround()-metodien käyttöä muuntaaksesi keston sopivimpiin ja ymmärrettävimpiin yksiköihin (esim. 90 minuutin muuntaminen "1 tunniksi ja 30 minuutiksi"). -
Erota sisäinen esitys näytöstä: Pidä sisäiset kestolaskelmasi tarkkoina
Temporal.Duration-tyypillä. Muunna ja muotoile näyttöä varten vasta käyttöliittymän renderöinnin yhteydessä, käyttäen omia muotoilufunktioita jaIntl-API:a globaalin tarkkuuden varmistamiseksi. - Pysy ajan tasalla: Seuraa Temporal API:n edistymistä ja selainyhteensopivuutta. Sen siirtyessä kohti täyttä standardointia ekosysteemi kehittyy, ja uusia työkaluja tai parhaita käytäntöjä saattaa ilmaantua.
- Ole tietoinen tarkkuudesta: Vaikka Temporal tukee nanosekunnin tarkkuutta, käytä vain sitä tarkkuutta, jota todella tarvitset. Suurempi tarkkuus voi joskus vaikeuttaa virheenkorjausta tai johtaa odottamattomaan pyöristykseen muunnettaessa matalamman tarkkuuden näyttöihin.
Johtopäätös
Temporal.Duration-tyypin esittely on merkittävä harppaus eteenpäin JavaScript-kehittäjille, jotka kamppailevat aikaväliaritmetiikan kanssa. Tarjoamalla muuttumattoman, eksplisiittisen ja globaalisti tietoisen API:n, Temporal vastaa vanhan Date-objektin pitkäaikaisiin rajoituksiin.
Voit nyt luottavaisesti suorittaa monimutkaisia aikalaskelmia, mitata tarkasti ajanjaksoja ja esittää kestoja tavalla, joka on sekä tarkka että kulttuurisesti sopiva käyttäjille maailmanlaajuisesti. Olitpa laskemassa ohjelmistojulkaisusyklin kestoa, jäljellä olevaa aikaa globaaliin tuotelanseeraukseen tai henkilön tarkkaa ikää, Temporal.Duration tarjoaa työkalut, joita tarvitset vankkojen ja luotettavien sovellusten rakentamiseen.
Ota Temporal.Duration käyttöön ja muuta tapaasi hallita aikaa JavaScript-projekteissasi. Ajan ja päivämäärän käsittelyn tulevaisuus JavaScriptissä on täällä, luvaten ennustettavamman, tehokkaamman ja globaalisti yhteensopivamman kehityskokemuksen.