Kompleksowy przewodnik po użyciu Temporal API w JavaScript do precyzyjnych i intuicyjnych obliczeń na przedziałach czasowych, od tworzenia duration po zaawansowaną arytmetykę i formatowanie.
JavaScript Temporal.Duration: Mistrzostwo w Obliczeniach na Przedziałach Czasowych
Temporal API w JavaScript wprowadza nowoczesny i potężny sposób na obsługę dat, czasów i przedziałów czasowych. Obiekt Temporal.Duration
reprezentuje długość czasu, zapewniając jasne i intuicyjne podejście do wykonywania obliczeń na przedziałach czasowych. Ten artykuł zagłębia się w szczegóły Temporal.Duration
, pokazując, jak tworzyć, manipulować i formatować okresy trwania dla różnych przypadków użycia.
Czym jest Temporal.Duration?
Temporal.Duration
reprezentuje odcinek czasu, wyrażając go w latach, miesiącach, dniach, godzinach, minutach, sekundach i ułamkach sekundy (milisekundach, mikrosekundach, nanosekundach). W przeciwieństwie do obiektów Date
, które reprezentują konkretny punkt w czasie, Temporal.Duration
reprezentuje ilość czasu. Jest zgodny z formatem czasu trwania ISO 8601 (np. P1Y2M10DT2H30M
oznacza 1 rok, 2 miesiące, 10 dni, 2 godziny i 30 minut). Temporal API zostało zaprojektowane tak, aby było bardziej intuicyjne i mniej podatne na błędy niż przestarzały obiekt Date
.
Tworzenie obiektów Temporal.Duration
Istnieje kilka sposobów na tworzenie obiektów Temporal.Duration
:
1. Z obiektu prostego (Plain Object)
Możesz utworzyć okres trwania, przekazując obiekt z pożądanymi właściwościami:
const duration = new Temporal.Duration(1, 2, 10, 2, 30, 0, 0, 0);
console.log(duration.toString()); // Wynik: P1Y2M10DT2H30M
Tworzy to okres trwania wynoszący 1 rok, 2 miesiące, 10 dni, 2 godziny i 30 minut. Zauważ, że argumenty odpowiadają następującej kolejności: years
(lata), months
(miesiące), weeks
(tygodnie), days
(dni), hours
(godziny), minutes
(minuty), seconds
(sekundy), milliseconds
(milisekundy), microseconds
(mikrosekundy), nanoseconds
(nanosekundy).
2. Z ciągu znaków ISO 8601
Możesz również utworzyć okres trwania z ciągu znaków ISO 8601 za pomocą Temporal.Duration.from()
:
const duration = Temporal.Duration.from("P1Y2M10DT2H30M");
console.log(duration.toString()); // Wynik: P1Y2M10DT2H30M
Jest to szczególnie przydatne przy pracy z okresami trwania przechowywanymi w formacie tekstowym lub otrzymanymi z zewnętrznego źródła.
3. Używając metod add()
i subtract()
z Temporal.Instant
, Temporal.ZonedDateTime
itp.
Gdy obliczasz różnicę między dwoma obiektami Temporal (np. Temporal.Instant
lub Temporal.ZonedDateTime
), wynikiem jest obiekt Temporal.Duration
. Na przykład:
const now = Temporal.Now.zonedDateTimeISO();
const later = now.add({ hours: 5 });
const duration = later.since(now);
console.log(duration.toString()); // Wynik: PT5H
Dostęp do składników Duration
Możesz uzyskać dostęp do poszczególnych składników obiektu Temporal.Duration
za pomocą jego właściwości:
const duration = Temporal.Duration.from("P1Y2M10DT2H30M");
console.log(duration.years); // Wynik: 1
console.log(duration.months); // Wynik: 2
console.log(duration.days); // Wynik: 10
console.log(duration.hours); // Wynik: 2
console.log(duration.minutes); // Wynik: 30
console.log(duration.seconds); // Wynik: 0
console.log(duration.milliseconds); // Wynik: 0
console.log(duration.microseconds); // Wynik: 0
console.log(duration.nanoseconds); // Wynik: 0
Wykonywanie operacji arytmetycznych na okresach trwania
Obiekty Temporal.Duration
obsługują dodawanie i odejmowanie za pomocą metod add()
i subtract()
. Metody te zwracają nowy obiekt Temporal.Duration
reprezentujący wynik operacji.
const duration1 = Temporal.Duration.from("P1Y2M");
const duration2 = Temporal.Duration.from("P3M4D");
const addedDuration = duration1.add(duration2);
console.log(addedDuration.toString()); // Wynik: P1Y5M4D
const subtractedDuration = duration1.subtract(duration2);
console.log(subtractedDuration.toString()); // Wynik: P10M26D
Możesz również łączyć te metody w łańcuchy dla bardziej złożonych obliczeń:
const duration = Temporal.Duration.from("P1D").add({ hours: 12 }).subtract({ minutes: 30 });
console.log(duration.toString()); // Wynik: P1DT11H30M
Metoda negated()
zwraca nowy obiekt Temporal.Duration
ze wszystkimi składnikami zanegowanymi:
const duration = Temporal.Duration.from("P1Y2M10DT2H30M");
const negatedDuration = duration.negated();
console.log(negatedDuration.toString()); // Wynik: -P1Y2M10DT2H30M
Metoda abs()
zwraca nowy obiekt Temporal.Duration
ze wszystkimi składnikami jako wartości dodatnie (wartości bezwzględne):
const duration = Temporal.Duration.from("-P1Y2M10DT2H30M");
const absoluteDuration = duration.abs();
console.log(absoluteDuration.toString()); // Wynik: P1Y2M10DT2H30M
Metoda with()
pozwala utworzyć nową instancję Temporal.Duration
z niektórymi lub wszystkimi właściwościami zmienionymi na nowe wartości. Jeśli wartość nie jest określona w obiekcie argumentu, użyta zostanie oryginalna wartość. Na przykład:
const duration = Temporal.Duration.from("P1Y2M10DT2H30M");
const newDuration = duration.with({ years: 2, days: 5 });
console.log(newDuration.toString()); // Wynik: P2Y2M5DT2H30M
Normalizowanie okresów trwania
Okresy trwania mogą być czasami wyrażone w formie nieznormalizowanej (np. P1Y12M
, co można uprościć do P2Y
). Metoda normalized()
próbuje uprościć okres trwania do jego najbardziej zwartej formy. Wymaga to jednak daty odniesienia, aby poradzić sobie ze złożonością zmiennej długości miesięcy. Aby poprawnie znormalizować, będziesz potrzebować instancji Temporal.PlainDate
, Temporal.ZonedDateTime
lub Temporal.Instant
.
Na przykład, normalizacja okresu trwania obejmującego miesiące i dni wymaga daty odniesienia:
const duration = Temporal.Duration.from("P1M32D");
const referenceDate = Temporal.PlainDate.from("2024-01-01");
const normalizedDuration = duration.normalized({ relativeTo: referenceDate });
console.log(normalizedDuration.toString()); // Wynik: P2M1D
W tym przykładzie okres P1M32D
jest normalizowany względem 1 stycznia 2024 r., co daje P2M1D
, ponieważ styczeń ma 31 dni.
Jeśli masz do czynienia tylko ze składnikami czasu (godziny, minuty, sekundy itp.), możesz normalizować bez daty odniesienia:
const duration = Temporal.Duration.from("PT25H61M");
const normalizedDuration = duration.normalized({ relativeTo: null }); //lub pomiń argument relativeTo
console.log(normalizedDuration.toString()); // Wynik: P1DT2H1M
Porównywanie okresów trwania
Możesz porównywać okresy trwania za pomocą metody compare()
. Metoda ta zwraca:
- -1, jeśli pierwszy okres jest krótszy niż drugi.
- 0, jeśli okresy są równe.
- 1, jeśli pierwszy okres jest dłuższy niż drugi.
const duration1 = Temporal.Duration.from("P1Y");
const duration2 = Temporal.Duration.from("P6M");
const comparisonResult = Temporal.Duration.compare(duration1, duration2);
console.log(comparisonResult); // Wynik: 1
Praktyczne przykłady
1. Obliczanie czasu do wydarzenia
Załóżmy, że chcesz obliczyć czas pozostały do określonego wydarzenia. Użyj Temporal.Now.zonedDateTimeISO()
, aby uzyskać bieżący czas, a następnie oblicz różnicę względem daty wydarzenia. Jeśli data wydarzenia już minęła, wynik będzie ujemny.
const eventDate = Temporal.ZonedDateTime.from({ timeZone: 'America/Los_Angeles', year: 2024, month: 12, day: 25, hour: 9, minute: 0, second: 0 });
const now = Temporal.Now.zonedDateTimeISO('America/Los_Angeles');
const durationUntilEvent = eventDate.since(now);
console.log(durationUntilEvent.toString()); // Wynik: np. P262DT14H30M (w zależności od bieżącej daty i godziny)
2. Śledzenie czasu trwania zadań w projekcie
W zarządzaniu projektami możesz użyć Temporal.Duration
do śledzenia szacowanego lub rzeczywistego czasu trwania zadań.
const task1EstimatedDuration = Temporal.Duration.from("PT8H"); // 8 godzin
const task2EstimatedDuration = Temporal.Duration.from("PT16H"); // 16 godzin
const totalEstimatedDuration = task1EstimatedDuration.add(task2EstimatedDuration);
console.log(`Całkowity szacowany czas trwania: ${totalEstimatedDuration.toString()}`); // Wynik: Całkowity szacowany czas trwania: P1DT
3. Obliczanie wieku
Chociaż precyzyjne obliczanie wieku wymaga uwzględnienia lat przestępnych i stref czasowych, Temporal.Duration
może zapewnić rozsądne oszacowanie:
const birthDate = Temporal.PlainDate.from("1990-05-15");
const currentDate = Temporal.PlainDate.from("2024-01-20");
const ageDuration = currentDate.since(birthDate, { smallestUnit: 'years' });
console.log(`Szacowany wiek: ${ageDuration.years} lat`); // Wynik: Szacowany wiek: 33 lat
4. Wyświetlanie okresów trwania w formacie czytelnym dla człowieka
Często trzeba wyświetlać okresy trwania w formacie czytelnym dla człowieka. Chociaż Temporal.Duration
nie ma wbudowanych funkcji formatujących, można stworzyć własną logikę formatowania:
function formatDuration(duration) {
const parts = [];
if (duration.years) parts.push(`${duration.years} ${duration.years === 1 ? 'rok' : 'lata'}`);
if (duration.months) parts.push(`${duration.months} ${duration.months === 1 ? 'miesiąc' : 'miesiące'}`);
if (duration.days) parts.push(`${duration.days} ${duration.days === 1 ? 'dzień' : 'dni'}`);
if (duration.hours) parts.push(`${duration.hours} ${duration.hours === 1 ? 'godzina' : 'godziny'}`);
if (duration.minutes) parts.push(`${duration.minutes} ${duration.minutes === 1 ? 'minuta' : 'minuty'}`);
if (duration.seconds) parts.push(`${duration.seconds} ${duration.seconds === 1 ? 'sekunda' : 'sekundy'}`);
return parts.join(', ');
}
const duration = Temporal.Duration.from("P1Y2M10DT2H30M");
const formattedDuration = formatDuration(duration);
console.log(formattedDuration); // Wynik: 1 rok, 2 miesiące, 10 dni, 2 godziny, 30 minut
Zaawansowane użycie i uwagi
1. Obsługa stref czasowych
Podczas pracy z przedziałami czasowymi, które przekraczają granice stref czasowych lub zmiany czasu letniego, kluczowe jest użycie Temporal.ZonedDateTime
w celu zapewnienia dokładnych obliczeń. Użycie Temporal.PlainDate
i Temporal.PlainTime
pozwoli uniknąć konwersji stref czasowych.
2. Najmniejsza jednostka i zaokrąglanie
Metody `since()` i `until()` często akceptują opcje definiujące najmniejszą jednostkę dla wynikowego okresu trwania. Na przykład, obliczając czas *do* wydarzenia i ograniczając wyniki do dni.
const eventDate = Temporal.PlainDate.from("2024-12-25");
const now = Temporal.PlainDate.from("2024-01-20");
const durationUntilEvent = now.until(eventDate, { smallestUnit: 'days' });
console.log(durationUntilEvent.toString()); //przykładowy wynik PT340D
3. Sekundy przestępne
Temporal nie uwzględnia sekund przestępnych natywnie. Jeśli wymagasz ekstremalnej precyzji, będziesz musiał obsługiwać sekundy przestępne osobno.
4. Strefy czasowe IANA
Temporal API opiera się na bazie danych stref czasowych IANA (Internet Assigned Numbers Authority). Upewnij się, że Twoje środowisko ma aktualną wersję bazy danych IANA, aby dokładnie obsługiwać konwersje stref czasowych.
Dobre praktyki
- Używaj formatu ISO 8601 dla ciągów znaków oznaczających czas trwania: Zapewnia to spójność i interoperacyjność.
- Wybierz odpowiedni typ Temporal: Użyj
Temporal.PlainDate
,Temporal.PlainTime
,Temporal.ZonedDateTime
lubTemporal.Instant
w zależności od tego, czy potrzebujesz wsparcia dla stref czasowych. - Normalizuj okresy trwania, gdy jest to konieczne: Normalizacja upraszcza okresy trwania i ułatwia ich porównywanie.
- Ostrożnie obchodź się ze strefami czasowymi: Konwersje stref czasowych mogą być złożone, więc używaj
Temporal.ZonedDateTime
i bądź świadomy zmian czasu letniego. - Rozważ najmniejszą jednostkę: Przy obliczaniu okresów trwania określ najmniejszą jednostkę, aby uzyskać pożądany poziom precyzji.
- Pisz testy jednostkowe: Dokładnie testuj swój kod, aby upewnić się, że obliczenia czasu trwania są dokładne.
Częste pułapki
- Ignorowanie stref czasowych: Nieuwzględnienie stref czasowych może prowadzić do nieprawidłowych obliczeń czasu trwania, zwłaszcza w przypadku wydarzeń w różnych lokalizacjach.
- Używanie przestarzałego obiektu Date: Przestarzały obiekt
Date
jest znany ze swoich dziwactw i niespójności. Preferuj Temporal API dla bardziej niezawodnej obsługi daty i czasu. - Brak normalizacji okresów trwania: Nienormalizowanie okresów trwania może komplikować porównania i obliczenia.
- Nieprawidłowy format ISO 8601: Użycie nieprawidłowego ciągu znaków ISO 8601 dla okresu trwania może powodować błędy.
Rzeczywiste przypadki użycia w różnych kulturach
Temporal API może być szczególnie korzystne w globalnych aplikacjach, gdzie różnice stref czasowych i niuanse kulturowe są znaczące. Oto kilka przykładów:
- Globalne planowanie wydarzeń: Dokładne planowanie wydarzeń w wielu strefach czasowych, z uwzględnieniem zmian czasu letniego. Na przykład, planowanie webinaru, który rozpoczyna się o 9:00 AM PST i wyświetlanie odpowiedniego czasu rozpoczęcia w różnych strefach czasowych, takich jak CET, JST i AEDT.
- Planowanie podróży międzynarodowych: Obliczanie czasu trwania podróży, w tym przesiadek i zmian stref czasowych. Jest to przydatne do generowania planów podróży i zarządzania rozkładami lotów. Na przykład, obliczanie całkowitego czasu podróży z Nowego Jorku do Tokio, wliczając przesiadkę w Londynie i dostosowując do różnic w strefach czasowych.
- Globalny e-commerce: Wyświetlanie szacowanego czasu dostawy w lokalnej strefie czasowej użytkownika. Wymaga to uwzględnienia strefy czasowej pochodzenia, czasu wysyłki i strefy czasowej docelowej. Na przykład, produkt wysłany z magazynu w Niemczech do klienta w Australii, z szacowanym czasem dostawy 7 dni, wyświetlany w lokalnym czasie klienta.
- Transgraniczne transakcje finansowe: Dokładne obliczanie naliczania odsetek lub terminów płatności w różnych regionach. Często wymaga to uwzględnienia różnych dni roboczych i świąt w każdym kraju. Na przykład, obliczanie odsetek naliczonych od pożyczki w Singapurze, z uwzględnieniem singapurskich świąt państwowych.
- Wielokulturowe aplikacje kalendarzowe: Obsługa różnych systemów kalendarzowych, takich jak kalendarz islamski czy hebrajski, oraz dokładne obliczanie czasu trwania wydarzeń i przypomnień na podstawie tych kalendarzy.
- Globalne zarządzanie projektami: Śledzenie czasu trwania zadań projektowych i terminów w rozproszonych zespołach, z uwzględnieniem różnych harmonogramów pracy i stref czasowych.
Wnioski
Temporal.Duration
zapewnia solidny i intuicyjny sposób pracy z przedziałami czasowymi w JavaScript. Rozumiejąc jego funkcje i dobre praktyki, możesz pewnie wykonywać dokładne i niezawodne obliczenia czasu trwania w swoich aplikacjach. Przyjęcie Temporal API prowadzi do czystszego, łatwiejszego w utrzymaniu kodu i zmniejsza ryzyko błędów związanych z przestarzałą obsługą daty i czasu.
Gdy będziesz zagłębiać się w Temporal API, pamiętaj o konsultowaniu oficjalnej dokumentacji i eksperymentowaniu z różnymi scenariuszami, aby w pełni zrozumieć jego możliwości. Dzięki nowoczesnemu projektowi i wszechstronnym funkcjom, Temporal zrewolucjonizuje sposób, w jaki obsługujemy daty, czasy i okresy trwania w JavaScript.