Po pohľade na Lambda výrazyStreamy v predchádzajúcich dieloch seriálu o Java 8 sa dnes pozrieme na jednu z menších, ale tiež pozoruhodných noviniek, ktoré prináša. Je ňou Datetime API, ktoré je pokusom (bohužiaľ už niekoľkým v poradí) priviesť do sveta Javy použiteľné API na prácu s dátumom a časom. Tentokrát to vyzerá, že by to mohlo byť ono.

Java má s dátumovým a časovým rozhraním dlhú a tak trochu nešťastnú históriu. Známa trieda java.util.Date prišla už vo verzii 1.0 aby sa ju následne vo verzii 1.1 pokúsila vystriedať trieda java.util.Calendar. Obe tieto rozhrania mali svoje nedostatky (kto po čase nechápavého debugovania zistil, že mesiace sa číslujú od nuly – teda január má číslo 0 a december číslo 11 – ruku hore). Obe sú neustále prítomné (aj keď pekne označené Deprecated anotáciou), čo ešte umocňuje chaos v tom, čo sa má používať. Oracle teda prišiel s novou verziou, tak sa poďme pozrieť, ako to s ňou vyzerá.

Základom všetkého sú triedy LocalDate, LocalTime, LocalDatetime, Instant, Duration a Period. LocalDate a LocalTime majú naozaj intuitívny názov a reprezentujú lokálny dátum alebo lokálny čas. LocalDatetime je potom ich kombináciou. Bez toho, aby sme prechádzali ich kompletné API, urobíme si z neho aspoň základnú ukážku:

Vytvorenie:

LocalDate date = LocalDate.of(2017, 3, 1); //<- Január má číslo 1!

Základný prístup k zložkám:

… = date.getYear();
… = date.getMonth();

Alebo niečo zaujímavejšie:

… = date.lengthOfMonth();    // <- počet dní v mesiaci
… = date.isLeapYear();          // <- je to priestupný rok?

Takže máme nejaké pekné triedy pre ľudí. A čo stroje? Pre nich je tu trieda Instant. Tá reprezentuje počet sekúnd od 1.1.1970. Nie len tu, ale v ďalších častiach API platí, že je rozdelené práve do týchto dvoch kategórií – API pre ľudské chápanie času a API pre strojové chápanie času.

Ak máme základný údaj o čase (bod na časovej osi), poďme sa pozrieť na intervaly. Tie sú reprezentované dvoma triedami: Period a Duration. Period je opäť to API určené pre ľudí, pretože je to interval definovaný v ľudských jednotkách:

LocalDate first = LocalDate.of(2017, Month.January, 1);
LocalDate second = LocalDate.of(2017, Month.February, 1);
Period twoDays = Period.between(first, second);

Duration je na druhej strane práve to API, ktorý pracuje s časovým intervalom tak, ako ho chápu stroje. Napríklad:

long ns = Duration.between(t1, t2).toNanos();

Nové Datetime API prináša tiež ďalšiu novinku a to sú takzvané Temporal Adjusters. Je to skupina tried, ktorá implementuje funkčné rozhranie TemporalAdjuster s jednou metódou adjustInto. Cieľom týchto tried je nejako modifikovať Temporal objekt (Temporal je spoločné rozhranie pre objekty, ktoré reprezentujú čas, teda aj LocalDate alebo Instant). Tá modifikácia môže vyzerať nasledovne:

  • nájdi prvý alebo posledný deň v danom mesiaci
  • nájdi prvý alebo posledný deň v nasledujúcom mesiaci
  • nájdi napr. nasledujúci utorok
  • atď.

Je to taká implementácia Strategy vzoru, kde každý objekt reprezentuje stratégiu modifikácie Temporal objektu.

Aby som bol presný, nie je to modifikácia, ale vygenerovanie niečoho nového. Celé API je totiž imutable, takže pôvodný objekt sa nemodifikuje, ale vracia sa nový. A to platí pre všetky metódy v API, ktoré by mali objekt modifikovať.

Nové Datetime API vyzerá sľubne. Po tých rokoch zbierania inšpirácie (aj od známej knižnice JodaTime) by to už Oracle mohol urobiť poriadne. Ak si uvedomíme, že časová zložka je veľmi často prítomná v každom bežnom informačnom systéme, tak fakt, že konečne je tu možno dobre navrhnutý nástroj na jeho zvládnutie, je celkom dobrá správa.