Fun with Javascript #2: Daty

Moja praca jest nieskończoną kopalnią inspiracji, a ostatnio mierzyłam się z datami w JavaScriptcie i postanowiłam zrobić na ten temat krótkiego posta. Główną atrakcyjną będzie obsługa błędów, ale zaczniemy od podstaw.

Zwróćcie uwagę, że to mój pierwszy post dla początkujących, ale może ktoś bardziej zaawansowany też znajdzie coś dla siebie.

UTC - co to?

UTC to tak zwany uniwersalny czas koordynowany, czyli czas bez przesunięcia związanego z południkami. Jest on identyczny jak znany nam Greenwich (GMT, Greenwich Mean Time). W Polsce używamy przesunięcia +2, czyli nasz godzina 16 (GTM +2) to w rzeczywistości 14 UTC (albo GTM +0).

Jest to szczególnie ważne w posługiwaniu się datami, gdyż często międzynarodowe systemy - żeby ułatwić back-endowe obliczenia - posługują się czasem bezwzględnym (koordynowanym). Jednak na frontcie powinno się robić przesunięcia dopasowane do strefy czasowej użytkownika.

Formaty zapisu daty

Zanim przejdziemy do sedna tego wpisu, czyli operacji na datach, musimy porozmawiać o formatach w których są przechowywane i wyświetlane. Wyróżniłam 3 typy, których zwykle używam: obiekt Date (przydatny od strony kodu, świetny do operacji na datach), format ISO (jako format bezpiecznego przechowywania dat) oraz przyjazny użytkownikowi string z datą :)

Obiekt Date

W każdym silniku obsługującym JavaScript istnieje obiekt Date. Jest bardzo charakterystyczny; chociaż pozwala na odczyt poszczególnych części taki jak dzień czy godzina - w zasadzie przechowuje liczbę milisekund od 1 stycznia 1970, godz. 00:00:00 UTC.

const nowaData = new Date();

Daty można ze sobą porównywać, data wcześniejsza jest większa od późniejszej:

const staraData = new Date('2019-07-24T17:46:49.695Z');
const nowszaData = new Date('2018-05-14T17:46:49.695Z');

nowszaData < staraData; // true

Nie będziemy się zagłębiać w operacje na datach, ale zwykle wyglądają w ten sposób:

const staraData = new Date('2019-07-24T17:46:49.695Z');
staraData.setHours(staraData.getHours() + 5));

Powyżej mamy zmianę daty o 5 godzin w przód :)

Format ISO

Jeśli muszę przechowywać datę w formacie tekstowym (na przykład w localStorage), najlepszy jest format ISO. Wygląda on tak: 2019-07-24T17:46:49.695Z. Jest bardzo fajny: niezależny od strefy czasowej, czytelny i krótki.

Jak go znaleźć w obiekcie Date i przetworzyć z obiektu Date?

// konwersja z obiektu klasy Date na stringa w formacie ISO
const obecnaData = new Date();
const formatISO = obecnaData.toISOString();

// konwersja z formatu ISO na obiekt klasy Date
const data =  new Date('2019-07-24T17:46:49.695Z')

StringDate

Mówiąc o datach na frontcie nie można zapomnieć o tym, że JavaScript pozwala na wyplucie daty w formie ładnej do czytania.

Istnieje dateString, który wygląda przyjaźnie dla ludzkiego oka: "Wed Jul 24 2019".

x.toDateString(); // "Wed Jul 24 2019"
x.toGMTString(); // "Wed, 24 Jul 2019 17:46:49 GMT"
x.toLocaleDateString(); // "24.07.2019"
x.toLocaleString(); // "24.07.2019, 19:46:49"
x.toLocaleTimeString(); // "19:46:49"
x.toString(); // "Wed Jul 24 2019 19:46:49 GMT+0200 (czas środkowoeuropejski letni)"
x.toTimeString(); // "19:46:49 GMT+0200 (czas środkowoeuropejski letni)"
x.toUTCString(); // "Wed, 24 Jul 2019 17:46:49 GMT"

Obsługa błędów

Bardzo długo można opisywać metody związane z klasą Date, ale nie jest to tematem dzisiejszego wpisu. Zerknijmy sobie na kod:

try {
  const nowaData = new Date(undefined);
  const staraData = new Date('bleble');
} catch(error) {
  console.log('ups błąd');
}

Niestety tak to nie działa. W zależności od przeglądarki istnieją różne implementacje, ale zamiast wyrzucać wyjątek, zapisuje się dziwny obiekt z informacją Invalid date.

W powyższym przypadku  nowaData i staraData będzie typu object, console.log(JSON.stringify(nowaData)); zwróci null. Co ciekawe isNaN(nowaData) zwróci true.

Więc najważniejsze pytanie: jak sprawdzić czy obiekt Date ma w sobie sensowną datę? Niestety nie ma jednego, dobrego wyjścia, najpopularniejsze w Internecie to sprawdzenie czy zmienna jest instancją klasy Date oraz czy jest NaN (not a number). Dlaczego? Bowiem new Date - tak jak wspomniała wcześniej - przechowuje liczbę całkowitą, która jest równa minisekundom od konkretnej daty.

const złaData = new Date('bleble');
if (złaData && złaData instanceof Date && !isNaN(złaData)) {
  console.log('to poprawna data')
} else {
  console.log('To nie jest poprawna data!')
}

W powyższym przykładzie wynikiem będzie informacja 'To nie jest poprawna data!'.

Trzeba jeszcze uważać na new Date(null), bowiem jest to prawidłowa data :) - 1 stycznia 1970, godz. 00:00:00 UTC.

Podsumowanie

Daty to bardzo obszarny temat, a jako że zdecydowałam się odrobinę skrócić posty (te dla początkujących) to na dzisiaj zdecydowanie wystarczy!

Bardzo dziękuję za wpisy, wsparcie, miłe komentarze i rady, jestem ogromnie wdzięczna za każde dobre i złe słowo. Osiągnęłam ponad 100 polubień na facebooku i otrzymałam mnóstw dobrych uwag, mam nadzieje, że uda mi się je wprowadzić w życie.

Kamila

Dziękuję za poświęcony czas, będzie mi bardzo miło jak zostawisz komentarz :)