Przedsprzedaż kursu SSJ: Super Senior Java rusza w listopadzie. Jeśli chcesz mieć najtaniej, zapisz się już teraz!Zapisz się →
📦 Ten wpis pochodzi z 2024 roku i jest częścią archiwum. Treść może być nieaktualna. ← Wróć do archiwum
architektura

Zakłopotanie związane z Instant oraz ZonedDateTime

#czas#rozterki#programowanie#architektura aplikacji

Zabawa z datami to odwieczny problem programistów. Nie inaczej ma to miejsce u mnie. Ostatnio musiałem się z tym zmierzyć w ramach swojego projektu Parking Domain. Problem dotyczył klasy ZonedDateTime oraz Instant, a dokładniej utworzenia Instant z określoną godziną. Jak wiemy Instant.now() reprezentuje punkt w czasie określony przez liczbę nanosekund liczoną od 1970-01-01T00:00:00Z. Z tego zapisu najbardziej istotna jest literka Z na końcu tej daty. Oznacza ona nic innego jak Zulu time. Korzystając z API klasy Instant poruszamy się w strefie czasowej UTC±00:00 tak naprawdę o tym nie wiedząc.

Jeśli uruchomimy Instant.now() w Polsce o godzinie 13:11 16 czerwca 2024 to otrzymamy - 2024-06-13T11:11:30.218825200Z. Jest on przedstawiony w strefie UTC uwzględniając przesunięcie.

Utrudnieniem staje się fakt, że na klasie Instant nie ma możliwości zmiany np. godziny. Musimy zrobić pewne obejście wykorzystując do tego ZonedDateTime.

ZonedDateTime.ofInstant(Instant.now(), ZoneId.systemDefault())
    .withHour(12)
    .withMinute(0)
    .withSecond(0)
    .withNano(0)
    .toInstant();

Będąc w Polsce spodziewamy się dostać w wyniku godzinę 12 czasu Europe/Warsaw zmienioną na godzinę 10 czasu UTC. I tak się właśnie dzieje - 2024-06-13T10:00:00Z.

Załóżmy teraz, że jesteśmy w strefie UTC i chcemy wykonać tą samą operację.

ZonedDateTime.ofInstant(Instant.now(), ZoneId.of("UTC"))
    .withHour(12)
    .withMinute(0)
    .withSecond(0)
    .withNano(0)
    .toInstant();

Bez niespodzianek, otrzymaliśmy 2024-06-13T12:00:00Z. Czyli dwie godziny później niż to miało miejsce w poprzednim przypadku, ponieważ ustawiliśmy godzinę będąc już w strefie czasowej Zulu. Jednak co jeśli chcielibyśmy w tym drugim przypadku przeskoczyć do strefy czasowej CET? Musimy skorzystać z metody withZoneSameInstant.

ZonedDateTime.ofInstant(Instant.now(), ZoneId.of("UTC"))
    .withHour(12)
    .withMinute(0)
    .withSecond(0)
    .withNano(0)
    .withZoneSameInstant(ZoneId.systemDefault())
    .toInstant();

Znowu otrzymaliśmy 2024-06-13T12:00:00Z. Dlaczego? Ponieważ linijka withZoneSameInstant(ZoneId.systemDefault()) zmieniła nam ZonedDateTime wykorzystując czas środkowoeuropejski, ale później skorzystaliśmy z metody toInstant, która ponownie zmieniła nam nasz czas uwzględniając strefę Zulu. Łatwo się w tym pogubić, ja właśnie tak miałem.

Nie ułatwia nam jeszcze kwestia tego, że do dyspozycji mamy również metodę withZoneSameLocal, która zmienia strefę zachowując czas.

ZonedDateTime.ofInstant(Instant.now(), ZoneId.of("UTC"))
    .withHour(12)
    .withMinute(0)
    .withSecond(0)
    .withNano(0)
    .withZoneSameLocal(ZoneId.systemDefault())
    .toInstant();

Dla takiego przypadku dostajemy oczywiście 2024-06-13T10:00:00Z. W ten sposób uzmysłowiłem sobie jak ważne jest przekazywanie odpowiedniej daty w samej aplikacji oraz pomiędzy systemami. Warto w odpowiedni sposób taką datę przekonwertować do czasu Greenwich i informować o tym klienty naszej aplikacji, aby nie było żadnych nieporozumień.

Znajdź mnie na