Nowe DateTime API
Nie rzadko w Twojej implementacji będziesz korzystał z danych, które będą wymagać przypisania do nich odpowiednich wartości dat czy czasu. Do JDK w wersji 7 programiści Java byli zmuszeni do korzystania z prostych i mało udanych implementacji zawartych w klasach Date i Calendar. Największe wyzwania jakie stały przed twórcami nowego DateTime API było:
- stworzenie prostego, przyjaznego API do programisty
- praca w środowisku wielowątkowym
- programowanie systemów działających w wielu strefach czasowych
LocalDate vs LocalTime vs LocalDateTime
Wszystkie trzy nowe klasy działają bardzo podobnie. Główną różnicą jest to, że jak sama nazwa wskazuje, jedna z nich odnosi się tylko do daty, następna do czasu, a ostatnia pracuje na wartościach zawierających zarówno czas jak i datę.
Najpierw pokażę Ci co potrafi samo LocalDate.
IntStream stream = IntStream.range(1, 10); stream.forEach(i -> { LocalDate date = LocalDate.of(2010 + i, Month.JANUARY, 1); System.out.println(date.getDayOfWeek()); });
Pierwszą ważną kwestią wartą wyjaśnienia jest nowy rodzaj strumieni, omawianych dokładniej w lekcji poprzedniej*. Klasa IntStream umożliwia w łatwy sposób tworzenie strumieni liczb całkowitych. W ten też sposób można wygodnie iterować unikając klasycznej pętli for. W samej pętli forEach, został utworzony obiekt date, który za pomocą metody of(), utworzy instancję klasy LocalDate w zależności od atrybutów, wypełnionych przez Ciebie w konstruktorze (są to odpowiednio: rok, miesiąc, dzień). W moim przypadku będzie to zawsze pierwszy dzień stycznia z zakresu lat 2010 – 2020. Na koniec wyświetlam dzień tygodnia, wykreowanej przeze mnie daty (oczywiście będzie to nazwa anglojęzyczna).
Efekt na konsoli:
SATURDAY SUNDAY TUESDAY WEDNESDAY THURSDAY FRIDAY SUNDAY MONDAY TUESDAY
Teraz pobawię się klasą LocalTime.
LocalTime localTime = LocalTime.now(); System.out.println("Actual time is: " + localTime + ", 10 hours before was: " + localTime.minusHours(10));
Korzystając z metody now() utworzyłem obiekt LocalDate, który reprezentuje aktualną datę w Twoim komputerze. API tej klasy, podobnie jak w poprzednim przykładzie udostępnia wiele przydatnych metod ułatwiających jej manipulację. W moim przykładzie skorzystałem z metody minusHours, aby wyświetlić datę o dziesięć godzin wcześniejszą. Tego typu metod jest o wiele więcej, ale z oczywistych względów nie będę ich wszystkich omawiał. Jeśli chcesz przejrzeć je wszystkie, jak zawsze polecam oficjalną dokumentację**.
Na koniec skorzystam z API LocalDateTime. Jest ono praktycznie takie samo jak w przypadku LocalTime, dlatego w tym przykładzie pokażę Ci inną ciekawą metodę query, dzięki której możesz wyciągać interesujące Cię fragmenty obiektu, poprzez użycie odpowiedniego zapytania.
LocalDateTime localDateTime = LocalDateTime.now(); System.out.println("Actual date is: " + localDateTime.query(TemporalQueries.localDate())); System.out.println("Actual time is: " + localDateTime.query(TemporalQueries.localTime()));
W tym przypadku skorzystano z gotowych „zapytań” zawartych w klasie pomocniczej TemporalQueries.
ZonedDateTime
Czasami jednak potrzebujesz do pracy konstrukcji pozwalającej na łatwe przetwarzanie aktualnej daty z jednej strefy czasowej do innej. Bardzo sprawnie zadziała tu klasa ZonedDateTime. Pewnym problemem może być wskazanie odpowiedniej nazwy pożądanej strefy czasowej, używanej w konstruktorze. Wynika to z tego, że jak się domyślasz, jest ich naprawdę dużo. Dlatego też do pomocy wykorzystam obiekt ZoneId, za pomocą którego łatwo sprawdzić, które strefy czasowe w jaki sposób zostały nazwane.
ZoneId.getAvailableZoneIds().stream() .filter(name -> name.contains("Europe")) .map(id -> ZoneId.of(id)) .forEach(x -> System.out.println(LocalDateTime.now().atZone(x)));
Powyższy kod wyświetli wszystkie strefy czasowe w Europie. Znając nazwy stref czasowych można utworzyć obiekt ZonedDateTime na kilka sposób. Pierwsza opcja to wpisanie danych daty i czasu manualnie, drugą możliwością jest użycie metody now podobnie jak w przypadku klasy LocalDate, trzecią skorzystanie z obiektu LocalDateTime. Ostatnia możliwość to sparsowanie Stringa, zawierającego odpowiednie dane. Jest to o tyle niewdzwięczne zadanie, że jedna literówka doprowadzi do wyrzucenia wyjątku DateTimeParseException.
ZonedDateTime zonedDateTime = ZonedDateTime.of(2021, 07, 31, 21, 31, 0, 0, ZoneId.of("Europe/Warsaw")); ZonedDateTime zonedDateTimeNow = ZonedDateTime.now(ZoneId.of("Europe/Warsaw")); ZonedDateTime zonedDateTimeFromLocal = ZonedDateTime.of(LocalDateTime.now(), ZoneId.of("Europe/Warsaw")); ZonedDateTime zonedDateTimeParsed = ZonedDateTime.parse("2021-07-31T21:31+02:00[Europe/Warsaw]"); System.out.println("ZonedDatetime created from of method " + zonedDateTime + "\nzonedDateTimeused now method " + zonedDateTimeNow + "\nzonedDateTime created from from localDateTime " + zonedDateTimeFromLocal + "\nzonedDateTime parsed from String " + zonedDateTimeParsed);
Samo API ZonedDateTime jest bardzo podobne do API LocalDateTime, także nie będę się nad nim dłużej rozwodził.
Konwersja ze starego zapisu
Czasami niestety będziesz pracować z kodem zastanym, który będzie zawierać datę i kalendarz. Na szczęście konwersja poprzednich obiektów do DateTime API nie stanowi wielkiego problemu.
Calendar calendar = Calendar.getInstance(); Date date = calendar.getTime(); System.out.println(LocalDateTime.ofInstant(date.toInstant(), ZoneId.systemDefault())); System.out.println(LocalDateTime.ofInstant(calendar.toInstant(), ZoneId.systemDefault()));
To wszystko jeśli chodzi o pracę z datą i czasem w Javie 8+. Co więcej jest to ostatnia lekcja z kursu Java 8. Jeśli tu udało Ci się dotrzeć, to gratuluję! Znasz już najpopularniejszą obecnie wersję JDK.
*Lekcja o Stream API: Java 8 #5: zastosowanie Stream API w przetwarzaniu danych
**Dokumentacja do nowego DateTime API:
https://docs.oracle.com/javase/8/docs/api/java/time/LocalTime.html
https://docs.oracle.com/javase/8/docs/api/java/time/LocalDate.html
https://docs.oracle.com/javase/8/docs/api/java/time/LocalDateTime.html
Kod do lekcji: https://github.com/developeronthego/java-jdk8/tree/master/src/main/java/java8/datetime