OCP Czas

UTC

Standard zapisu czasu, wykorzystujący uśredniony czas słoneczny zerowego południka. Nie mylić z GMT które jest strefą czasową, tym bardziej nie mylić z czasem londyńskim (po przejściu na czas letni Londyn ma przesunięcie +1). Do wyznaczania czasu w standardzie UTC wykorzystywanych jest ponad 400 zegarów atomowych które co jakiś czas są korygowane o sekundę przestępną.

Sekunda przestępna

W wyniku nieregularnego zwalniania obrotu ziemi wokół własnej osi czas UTC rozsynchronizowuje się z czasem słonecznym. By zniwelować tą różnicę ogłaszane jest wystąpienie sekundy przestępnej (z wyprzedzeniem). Dodatnia sekunda „cofa” czas przez wystąpienie 23:60. Ujemna sekunda przeskakiwałaby jedną sekundę ale jeszcze nie wystąpiła.
Sekunda przestępna przeczy założeniu, że minuta ma 60 sekund co może powodować błędy.
Istnienie sekundy przestępnej sprawia, że nie ma możliwości dokładnego obliczenia ilości sekund między dwiema datami bez informacji o sekundach przestępnych (wyjątek data Juliańska).

Java Time Scale

System zapisu czasy stosowany w Javie.

    • Założenia:
  • pokrywa się z czasem UTC w południe każdego dnia
  • ma dwie wersje: przed 1972-11-03 pokrywa się z czasem słonecznym zerowego południka, po pokrywa się z UTC-SLS

Implementacja UTC-SLS

Może być wykonana po stronie systemu na którym zainstalowany jest JVM albo w JVM.
Nie ma sekund przestępnych. Jeśli implementacja jest po stronie systemu wtedy czas systemowy jest zwracany w System.currentTimeMillis() a przez to w wyniku wywołania wszystkich now() na obiektach czasu. Jeśli tak nie jest JVM rozciąga czas trwania ostatnich 1000 sekund dnia o 1/1000 sekundy.

ZonedDateTime

Zawiera LocalDateTime i ZoneId. Wszystkie obiekty czasu są bezpieczne wielowątkowo i niezmienne.

Na podstawie ZonedDateTime można tworzyć instancje w innych strefach czasowych:
withZoneSameInstant(zone) po prostu przypisuje LocalDateTime inną strefę czasową
withZoneSameInstant(zone) mówi jaka godzina jest teraz w podanej strefie czasowej

LocalDateTime

Godzina i data bez strefy czasowej, zawiera LocalDate i LocalTime. Oba obiekty udostępniają metody do utworzenia LocalDateTime np. atTime(), atStartDay() lub atDate().
Zawierają metody until() służące do obliczania różnicy między obiektami w jednostkach podanych jako drugi parametr. Wykonanie jest delegowane do kodu jednostki ChronoUnit.
Zwracają zmodyfikowaną datę przez plusJednostka() i minusJednostka() np. minusSeconds(), plusWeeks().

ZoneId

Reprezentuje strefę czasową, która składa się z różnicy w stosunku do czasu GMT (ZoneOffset) oraz reguł zmiany czasu (ZoneRules).

ZoneOffset

Przechowuje różnice w sekundach cache’ując wielokrotności 15 minut oraz dotychczas użyte Offsety.

ZoneRules

Zawierają informację o zmianach czasu podzielone na dwie grupy:
ZoneOffsetTransitionRules algorytmiczne reguły zmiany czasu, np ostatnia niedziela marca
ZoneOffsetTransition dotychczasowe historyczne zmiany czasu oraz zmiany wynikające z zastosowania ZoneOffsetTransitionRules w danym roku, zawierają Offsety przed i po zmianie oraz czas zmiany.

Jeśli obiekt ZoneDateTime jest tworzony na podstawie LocalDateTime wtedy mogą wystąpić trzy przypadki:

  • normalny: dla tej daty i strefy czasowej występuje tylko jedno przesunięcie w stosunku do GMT
  • godzina nie istnieje: jest pomijana w czasie zmiany czasu na letni
  • godzina występuje podwójnie: zegar jest cofany przy zmianie czasu na zimowy

W drugim przypadku do „nieistniejącej” godziny dodawana jest wartość zmiany czasu.

W trzecim przypadku domyślnym zachowaniem jest ustawienie godziny ze „starym” przesunięciem. Można wpływać na to zachowanie metodami withEarlierOffsetAtOverlap (domyślne zachowanie) oraz withLaterOffsetAtOverlap. Metody zwrócą nowy obiekt z alternatywną wartością przesunięcia. Earlier znaczy „wcześniejsze: (przed zmianą czasu) nie „o mniejszej wartości”. Alternatywnie można wykorzystać parametr prefferedOffset – jeśli wśród praw przesunięć będzie podana wartość, wtedy zostanie wybrana.

Informacje o zmianie daty są dostarczane z IANA Time Zone DataBase. Nowsza wersja jest dostarczana z updatem jdk ale może być podmieniona ręczniie (jdk/jre/lib/tzdb.dat).

Przy tworzeniu ZoneDateTime na podstawie Instant nie ma niejednoznaczności.

Instant

Przechowuje ilość sekund (long) i nanosekund (int) od początku ery (1970-01-01T00:00:00Z). Ma metody plusSeconds(), plusMillis() i plusNanos() i jedną która przyjmuje ilość i jednostkę. Czas jest zapisany w strefie GMT – nie występują komplikacje związane ze zmianą czasu – przesunięcie jest stałe (zerowe). Stosowany np. jako timestamp.

TemporalAdjuster

Można stosować poprzez wywołanie metody adjustInto(temporal) na obiekcie TemporalAdjuster lub wywołując
with(thisAdjuster) na obiekcie Temporal (wywołanie jest delegowane do Adjustera).

Domyslne Adjustery umieszczone w TemporalAdjusters

  • firstDayOfMonth
  • lastDayOfMonth
  • firstDayOfNextMonth
  • lastDayOfYear
  • firstDayOfNextYear
  • firstInMonth(Dzien tygodnia)
  • lastInMonth(DzienTygodnia)
  • next(DzienTygodnia)
  • previous(DzienTygodnia)
  • previousOrSame(DzienTygodnia)
  • lastInMonth(DzienTygodnia)
  • dayOfWeekInMonth (licznik, DzienTygodnia)

Licznik w ostatniej opcji oznacza które to ma być wystąpienie dnia tygodnia w miesiącu. Może wyjść poza zakres miesiąca do następnego: np 8 wtorek stycznia da luty;
-1 to ostatnie wystąpienie tego dnia tygodnia w tym miesiącu, -2 to przedostatnie itd (można cofnąć się do poprzedniego miesiąca)
0 to ostatnie wystąpienie w poprzednim miesiącu

Period

Zawiera pola int: year, month, day. Pola mogą mieć różne znaki. Dodanie Period.of(0, -1, 16) odejmie miesiąc i doda 16 dni. Nie można dodawać go do LocalTime. Bierzę pod uwagę zmiany czasu przy dodawaniu go do ZonedDateTime.
Nie można chainować Period.of (to statyczna metoda).
Ma metody plusYears(), minusDays() itd. które już są instancyjne.
Period domyślnie nie jest normalizowany tzn 24 miesiące nie są zamieniane na 2 lata. Służy do tego metoda normalized().

Inne przydatne metody:
toTotalMonths(), multipliedBy(), negated(), between()
Period toString() wyświetla PxYyMzD ale parse() przyjmuje tez W – tygodnie i może mieć minus przy dowolnej sekcji.

Duration

Zawiera pola long seconds i int nanos.
Ma metody analogiczne do Period oraz pomocniczne:plusDays(), plusHours(). Pobranie sekund i nanosekund ma przedrostek get…(), pozostałe jednostki mają metodę to…().
Ujemne nanosekundy są odejmowane od pełnych sekund. Sekundy mają metody związane z ich znakiem isNegative(), isNegative(), abs(). Dodawanie Duration nigdy nie bierze pod uwagę zmiany czasu.
toString() wyświetla PTsekundy.nanoSekundyS
parse() może przyjmować niecałkowite sekundy

Clock

Clock pozwala zastapic czas sytemowy.
Domyślne implementacje to:

  • fixed – cały czas zwraca jedną wartość
  • offset – opakowuje inny zegar i dodaje/odejmuje od niego stała wartość
  • tick – zaokrągla o wskazanie w Duration
  • tickSeconds i tickMinutestick w którym Duration to seconda i minuta

Zegar zawiera informację o strefie czasowej.

Inne klasy

MonthDay – ma metodę isValidYear(year) która dotyczy tylko 29 lutego, czyli pokrętnie sprawdza czy rok jest przestępny
YearMonth – ma metodę do tego samego nazwaną isLeapYear() i drugą do sprawdzania czy dzień istnieje w miesiącu – isValidDay(day)
OffsetDateTime – ZonedDateTime pozbawione reguł zmiany czasu, składa się z OffsetTime i LocalDateTime.

ChronoLocalDate

Interfejs do przechowywania daty w dowolnym kalendarzu. Zalecanie jest nie używanie go, posługiwanie się LocalDate a tłumaczenie do innych kalendarzy traktować jako lokalizacje. Pisanie aplikacji posługującej się abstrakcyjnym kalendarzem będzie podatne na błędy przez nieświadome stosowanie założeń dotyczących kalendarza gregoriańskiego: stała ilość miesięcy w roku, podobna długość miesięcy, rozpoczynanie miesiąca od pierwszego dnia, mniejszy numer roku oznaczający wcześniejszy rok.

Data Juliańska

Przy stosowaniu kalendarza ciężko obliczyć liczbę dni między dwoma datami. Dodatkowo jeśli jedna z dat jest p.n.e a druga n.e. od wyniku trzeba odjąć 1 ponieważ w tym aktualnym sposobie zapisu dat nie występuje rok 0. Tzn. po 1 p.n.e występuje rok 1 n.e. W czasie powstawania koncepcji „naszej ery” nie istniała koncepcja zera.
Powoduje to problemy z liczeniem dlatego powstała data Juliańska (JD – nie mylić z kalendarzem Juliańskim). Zakłada wybranie roku zerowego (konkretnie 4713 p.n.e) a następnie zapisywanie dat jako ilości dni które minęły od tego czasu (w oryginalnym formacie dzień zaczynał się w południe). Godziny są zapisywane jako części ułamkowe dnia. Data stosowana np. w astronomii, pomysł został zaadoptowany przez programistów Unixa (z uwzględnieniem sekundy przestępnej). Stosowana jest zmodyfikowana data Juliańska (MJD) która przesuwa początek epoki 2400000,5 dni do przodu, żeby operować na mniejszych liczbach. Połówka na końcu odzwierciedla, że w MJD dzień zaczyna się o północy. Java ma implementacje obu dat w klasie JulianFields ale nie wspiera części ułamkowych – godzin.

Pomiar czasu a bezpieczeństwo

Pomiar odchyleń od prawidłowego czasu umożliwia przeprowadzanie ataków deanonimizujących. Zwiększenie obciążenia anonimowego serwera wpływa na jego temperaturę, temperatura wpływa na działanie oscylatora co tworzy odchylenia, sprawdzając czas „podejrzanych” serwerów można wnioskować który z nich działa również w anonimowej sieci.

Pomiar czasu przetwarzania danych może, w niektórych przypadkach, zdradzać informacje na temat samego przetwarzania.
Przykładowo: aplikacja porównując hasła (zamiast hashy) po jednym znaku (zamiast całych) zwróci wynik po czasie proporcjonalnym do liczby prawidłowo wpisanych znaków.

Źródła:
Dokumentacja pakietu java.time
www.timeanddate.com UTC
www.timeanddate.com Sekunda przestępna
Internet Draft UTC-SLS
Hot or Not: Revealing Hidden Services by their Clock Skew Steven J. Murdoch