Polski

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:

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

Częste pułapki

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:

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.